Best answer

How to use Retrieve Poll trigger when all API calls require a session id from an inital login?

  • 14 December 2020
  • 10 replies
  • 972 views

Userlevel 1

I’m a bit new to this and working with an API that requires a separate username/password login process in order to establish a session ID as a keytoken for use in authentication with all other API calls. Each session only lasts 60 minutes after the last API call.

 

I know I could use a scheduled trigger to automate the login process first, but my preference would be to only run the zap when a new record is found in the data I would like to poll.

 

Is this a common issue or something that is easily handled within the trigger fields that I’m not seeing?  Thanks!

 

 

icon

Best answer by ikbelkirasan 15 December 2020, 00:39

View original

This post has been closed for comments. Please create a new post if you need help or have a question about this topic.

10 replies

Userlevel 7
Badge +14

What app API are you using?

Does the app offer Webhooks? (This would trigger the Zap each time there is a new record, rather than having to poll the app API.)

 

Here’s a help article about Webhooks Retrieve Poll: https://zapier.com/help/doc/how-get-started-webhooks-zapier#using-the-retrieve-poll-trigger-option

Userlevel 1

Hey @Troy Tessalone - I was attempting to use the Zapier Webhooks app to Retrieve Poll from a URL I don’t have the option to get the data sent to a Zapier webhook for use with “Catch hook” triggers.

 

Thanks for the article link - found the limitation here:

This trigger is one that only supports basic authentication - should you need to create your own polling triggers for APIs that offer advanced forms of authentication, you should create your own private application on the Zapier platform.

 

I guess it makes sense to dive into that and see what I can do. Thanks so much!

Userlevel 1

SO I got pretty far into the “session authentication” portion of building my own private app except for the returning of the session token.

 

Here is the code that Zapier provides:

return z.request(options)
.then((response) => {
response.throwForStatus();
const results = response.json;

// You can do any parsing you need for results here before returning them

return {
'sessionKey': results.sessionKey
};
});

Here’s the response from the logs - you’ll see that the login is successful and the ‘SessionID’ is provided in the output but I have no clue how to parse the sessionID and get it into the ‘SessionKey’ using the default code above.

 

{"Columns":[{"Name":"Success","Type":"Int16"},{"Name":"SessionID","Type":"String"},{"Name":"Permission","Type":"String"},{"Name":"Canadian","Type":"Int16"},{"Name":"Internal","Type":"Int16"},{"Name":"International","Type":"Int16"},{"Name":"Account","Type":"String"},{"Name":"Store","Type":"String"},{"Name":"Email","Type":"String"},{"Name":"FirstName","Type":"String"},{"Name":"LastName","Type":"String"},{"Name":"Email","Type":"String"}],"Rows":[[1,"57EE00E41FA808B56E567B28BFD38EA515C3E0AF47E2D687AD611366CF7703B2","30",0,0,0,":censored:6:fbf1d817b6:","","myemail@sample.com","Jack","Ryan","myemail@sample.com"]]}

 

Userlevel 7
Badge +12

@dstepchew - You can extract the session ID using the following code snippet:

return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;

const sessionIdIndex = results.Columns.findIndex(
(column) => column.Name === "SessionID"
);
const sessionId = results.Rows[0][sessionIdIndex];

return {
sessionKey: sessionId,
};
});

 

Userlevel 1

@ikbelkirasan - That worked great! Got the authentication piece set up and successfully tested.

Now onto the trigger - On step 1 “Configure API Request” I have the necessary headers, params, and body - also figured out how to parse the response content from another article:

const options = {
url: bundle.inputData.url,
method: 'POST',
headers: {
'Content-Type': 'text/plain'
},
params: {
'content-type': 'text'
},
body:

{"Columns":[{"Name":"SessionID","Type":"String"},
{"Name":"Account","Type":"String"},
{"Name":"OrderStatus","Type":"String"}],
"Rows":[[bundle.authData.SessionID,bundle.authData.Account,bundle.inputData.OrderStatus]]
}

}

return z.request(options)
.then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content);

// You can do any parsing you need for results here before returning them

return results.Rows;
});

Then I go to the next step - Test your API request, and I’m getting a response that the SessionID is missing.

Oddly, looking at the ‘HTTP’ logs, I see a successful authentication step and another step showing a successful response with the array I’m looking for. Why would I be getting this missing session ID error if I’m seeing a valid response in the logs? 

 

Userlevel 7
Badge +12

@dstepchew That’s because bundle.authData.SessionID doesn’t exist. The session ID is actually stored in bundle.authData.sessionKey.

 

Userlevel 1

@ikbelkirasan OK that worked. Now I’m up against the “Got a result missing the ‘id’ property” error. Looking at this response I noticed that the user was able to establish the id for result using the values provided in each result, but my issue is that the column definitions are sent in a separate array. I tried to repurpose the same method used in the login help provided above but to no avail - getting error ‘cannot ready property of ‘number’ of undefined.’

 

Here is a sample of the results:

{
"Columns": [
{
"Name": "SalesOrder",
"Type": "Int64"
},
{
"Name": "SalesOrderPrefix",
"Type": "String"
},
{
"Name": "CustPurchaseOrder",
"Type": "String"
},
{
"Name": "Store",
"Type": "String"
},
{
"Name": "UnitsOrder",
"Type": "Int32"
},
{
"Name": "UnitsShip",
"Type": "Int32"
},
{
"Name": "UnitsCancel",
"Type": "Int32"
},
{
"Name": "UnitsPacked",
"Type": "Int32"
},
{
"Name": "UnitsOpen",
"Type": "Int32"
},
{
"Name": "ShipComplete",
"Type": "String"
},
{
"Name": "CancelBackOrder",
"Type": "String"
},
{
"Name": "Total",
"Type": "Decimal"
},
{
"Name": "Terms",
"Type": "String"
},
{
"Name": "OrderDate",
"Type": "String"
},
{
"Name": "CancelDate",
"Type": "String"
},
{
"Name": "Status",
"Type": "String"
},
{
"Name": "TrackingCode",
"Type": "String"
},
{
"Name": "ShipVia",
"Type": "String"
},
{
"Name": "TrackingLink",
"Type": "String"
},
{
"Name": "IntTrackingLink",
"Type": "String"
},
{
"Name": "PktCtrlNbr",
"Type": "String"
},
{
"Name": "MinShipDate",
"Type": "String"
},
{
"Name": "Address1",
"Type": "String"
},
{
"Name": "Address2",
"Type": "String"
},
{
"Name": "City",
"Type": "String"
},
{
"Name": "State",
"Type": "String"
},
{
"Name": "Zip",
"Type": "String"
},
{
"Name": "Country",
"Type": "String"
},
{
"Name": "FeeType",
"Type": "String"
},
{
"Name": "FeeTotal",
"Type": "Decimal"
},
{
"Name": "OrderLevelNote",
"Type": "String"
},
{
"Name": "Page",
"Type": "String"
}
],
"Rows": [
[
17924398,
"SO",
"SO1153678",
"",
1,
0,
0,
1,
0,
"0",
"0",
237.73,
"N30",
"12/14/2020",
"12/14/2021",
"In Packing",
"",
"UP10",
"",
"",
"",
"12/14/2020",
"Test Test",
"1Test parkway",
"New York",
"NY",
"11111",
"US",
"DROP2",
2.50,
"",
"1-1"
]
]
}

 

Here is my code right now:

const options = {
url: bundle.inputData.url,
method: 'POST',
headers: {
'Content-Type': 'text/plain'
},
params: {
'content-type': 'text'
},
body:

{"Columns":[{"Name":"SessionID","Type":"String"},
{"Name":"Account","Type":"String"},
{"Name":"OrderStatus","Type":"String"}],
"Rows":[[bundle.authData.sessionKey,bundle.authData.Account,bundle.inputData.OrderStatus]]
}

}

return z.request(options)
.then((response) => {
response.throwForStatus();
//const results = response.json;
const results = z.JSON.parse(response.content);

const SalesOrderIndex = results.Columns.findIndex(
(column) => column.Name === "SalesOrder"
);
number = results.Rows[0][SalesOrderIndex];
// const number = results.Rows[0];
const Rows = results.Rows.map((obj) => {
const p = obj.Row;
p.id = p.number;
return p;
});

// You can do any parsing you need for results here before returning them

return Rows;
});

I’m so close! So appreciative of your support here.

Userlevel 7
Badge +12

@dstepchew - You’re welcome! Ok, in that case, you would do it like so:

const options = {
url: bundle.inputData.url,
method: "POST",
headers: {
"Content-Type": "text/plain",
},
params: {
"content-type": "text",
},
body: {
Columns: [
{ Name: "SessionID", Type: "String" },
{ Name: "Account", Type: "String" },
{ Name: "OrderStatus", Type: "String" },
],
Rows: [
[
bundle.authData.sessionKey,
bundle.authData.Account,
bundle.inputData.OrderStatus,
],
],
},
};

return z.request(options).then((response) => {
response.throwForStatus();
const results = z.JSON.parse(response.content);

// Convert to an object
const item = results.Columns.reduce((prev, current, index) => {
prev[current.Name] = results.Rows[0][index];
return prev;
}, {});
item.id = item.SalesOrder;

return [item];
});

 

Userlevel 1

@ikbelkirasan - Result was exactly what I needed. Setting this up now as a new Zap. Initial test looks great. Thanks so much.

Userlevel 7
Badge +12

@dstepchew - That’s great news!

One last thing, can you update the best answer selection because it seems that you chose the wrong one :)