I’m trying to configure a Webhook Listener in Celigo to accept POST requests that include an Authorization: Bearer <token> header. However, it doesn’t seem like Celigo supports this authentication method.
What I'm Trying to Do:
Receive a webhook POST request with a Bearer Token in the header.
Validate the token.
Send the data to NetSuite.
Right now, the only workaround I see is using an external API (e.g., FastAPI, AWS Lambda) to intercept and validate the request before forwarding it to Celigo, but I’d prefer to handle this directly in Celigo if possible.
Is there a way to configure the Webhook Listener to accept and validate Bearer Tokens? If not, has anyone found a better workaround?
Three ways you could do this without an enhancement (which we should do):
Use a MyAPI - since MyAPIs are behind a bearer token only, this could work if you can give your source system the API token that you generate under resources > API token > custom access to only the MyAPI. From here, the MyAPI can take the request and directly forward it to an IO listener sitting on a flow. Below is a sample script I had made for a different use case, but you should get the gist of how to do it from here. Notice the exports.run call out to the IO listener.
/*
* handleRequest function stub:
*
* The name of the function can be changed to anything you like.
*
* The function will be passed one 'options' argument that has the following fields:
* 'method' - http request method (uppercase string).
* 'headers' - http request headers (object).
* 'queryString' - http request query string (object).
* 'body' - parsed http request body (object, or undefined if unable to parse).
* 'rawBody' - raw http request body (string).
*
* The function needs to return a response object that has the following fields:
* ‘statusCode’ - http response status code (number).
* 'headers' - http response headers overrides (object, optional).
* 'body' - http response body (string or object).
* Throwing an exception will signal an error.
*/
import {request, integrations, exports, imports, flows, connections} from 'integrator-api';
var productIntegrationId = "65f10eda70ff6d8e69e527db";
function handleRequest (options) {
let responseBody = "";
if (!options.queryString.validationToken) {
let productIntegrationObj = integrations.get(productIntegrationId);
if (productIntegrationObj && productIntegrationObj.settings && productIntegrationObj.settings.ioWebhookListeners && Array.isArray(productIntegrationObj.settings.ioWebhookListeners) && productIntegrationObj.settings.ioWebhookListeners.length > 0) {
let listenerExportIdsSubscribed = productIntegrationObj.settings.ioWebhookListeners;
for (let e of listenerExportIdsSubscribed) {
let exportObj = exports.get(e);
if (exportObj && exportObj.webhook && exportObj.webhook.provider === "integrator-extension") {
options.body.received_at = (new Date()).toISOString();
try {
exports.run({
_id: e,
listenerData: [options.body]
})
} catch (e) {
console.log(JSON.stringify(e));
}
}
}
}
} else {
responseBody = options.queryString.validationToken;
}
return {
statusCode: 200,
headers: {"Content-Type":"text/plain"},
body: responseBody
}
}
In the March release coming this week, you'll be able to access request headers and queryParams that are sent to the webhook. So in this scenario, you would set up the webhook as a "secret URL," then within transformation 2.0, you could compare the token with your correct one. I wouldn't necessarily recommend this though because you'd be storing the token in plain text as there isn't somewhere to put an encrypted field setting for webhooks.
You could set up the webhook as a secret URL, then just ignore the authorization header altogether. The fact that the webhook URL is already a secret would be some level of security.
Thank you! I'm new to Celigo and having trouble following the first option. I have a few questions:
Is the Integration ID referring to the folder where our flow is located?
I now have two URLs—one for MyAPI and one for the Celigo Listener. Are these different from the Webhook Listener? They seem to be separate.
I’m not quite understanding how MyAPI and the Celigo Listener work together in this setup. Could you outline a super simple flow configuration to validate that I can send a POST request and inspect the results?
For the MyAPI route, you are essentially using it as a middleman step as opposed to AWS Lambda or FastAPI. So your source is calling the MyAPI, which uses bearer token authentication, and then it's passing the received data to the flow with the listener. With MyAPI, you would:
Make a flow with an IO listener (not a webhook - see prior post screenshots).
Copy the export ID of the listener you made, which is the part between the /exports/XXXXX/data.
Turn the flow on and make a dummy import step if you have to for now.
Make the MyAPI and use the export ID you previously copied in the variable below.
Copy the MyAPI URL that is generated.
Go to API Tokens and make a token that only has access to the MyAPI.
Give your source the MyAPI URL and the token made.
Your MyAPI script would be much simpler and would just be something like this:
Thank you, really appreciate the quick responses. I think I have this set up correctly although I am not having any luck with setting up a "Dummy" import, although that is likely just me being a novice with the platform.
For a flow to be fully "on" and operational, you need at least one export and one import, even after enabling the flow. Your listener is the export, and when I say "dummy" import, I simply mean creating any import step—working or not. I often use our mirror endpoint: Post and receive sample data with the Mirror API endpoint. You can also just make an import going to the destination you need this payload to go to, but I'm not sure where you're trying to send this.