I’m trying to integrate SAP Business One (SAP B1) Service Layer API with Airtable scripting to fetch business partner data. The integration works perfectly in Postman and Python, but when I run the same API request inside an Airtable scripting block, I get the following error:
401 - Invalid session or session already timeout.
What I’ve Tried:
-
Confirmed that it works in Postman and Python
-
I can successfully authenticate and fetch data from SAP B1.
-
Session ID is returned properly, and I manually add the
ROUTEID
.
-
-
Using
remoteFetchAsync()
in Airtable Scripting-
I correctly pass
Content-Type
andCookie
headers. -
Even when I manually copy a working session ID from Postman, I still get a 401 Unauthorized error
-
-
Has anyone successfully integrated SAP B1 with Airtable scripting?
-
Could Airtable be modifying the request headers in a way that causes SAP B1 to reject it?
-
Is there any alternative way to bypass CORS while using Airtable scripting?
-
Any other troubleshooting steps I should try?
Code:
// Step 1: Login to SAP B1
const SAP_LOGIN_URL = "https://serverb1s/v2/Login";
const SAP_USERNAME = "username";
const SAP_PASSWORD = "pwd";
const SAP_COMPANY_DB = "DB";
let sessionId = null;
let routeId = null;
async function loginToSAP() {
output.text("Logging in to SAP B1...");
try {
let response = await remoteFetchAsync(SAP_LOGIN_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
UserName: SAP_USERNAME,
Password: SAP_PASSWORD,
CompanyDB: SAP_COMPANY_DB
})
});
output.text("Login Response Status: " + response.status);
if (!response.ok) {
let errorText = await response.text();
throw new Error(`SAP B1 Login Failed: ${response.status} - ${errorText}`);
}
let data = await response.json();
sessionId = data.SessionId;
// Manually extract ROUTEID from Postman Cookies tab
routeId = ".node2"; // Replace with the actual ROUTEID from Postman
output.text(`Login successful. Session ID: ${sessionId}, ROUTEID: ${routeId}`);
return { sessionId, routeId };
} catch (error) {
output.text("Login errror: " + error.message);
throw error;
}
}
// Step 2: Fetch Data from SAP B1 using both cookies with correct syntax
async function fetchSAPData() {
output.text("Fetching data...");
const SAP_DATA_URL = "https://hsl149.myc4p.com/b1s/v2/BusinessPartners?$select=CardCode,CardName";
// Ensure we have a session
if (!sessionId) {
await loginToSAP();
}
console.log(sessionId);
console.log(routeId)
try {
let headers = {
"Content-Type": "application/json",
"Cookie": `B1SESSION=${sessionId};ROUTEID=.node2`
};
output.text("Sending Request with Headers: " + JSON.stringify(headers, null, 2));
let response = await remoteFetchAsync(SAP_DATA_URL, {
method: "GET",
headers: headers,
body: null
});
output.text("Response Status: " + response.status);
let data = await response.json();
output.text("Fetched Data: " + JSON.stringify(data, null, 2));
if (!response.ok){
throw new Error(`Failed to fetch SAP data: ${response.status}`);
}
console.log(data.value);
} catch (error) {
output.text("Error during data fetch: " + error.message);
throw error;
}
}
// Run the function
await fetchSAPData();