Airtable's Script POST Request to API Results in 405 Error - Getting OPTIONS Instead of POST

I’ve encountered an issue with my Airtable script that attempts to send POST requests to my Node.js API. The problem is that instead of a POST, my API receives an OPTIONS request, leading to a 405 Method Not Allowed error.

Working example: I’ve confirmed that the API endpoint operates correctly when tested using Postman.

What’s not working: When executing the script from Airtable, I get an error related to Cross-Origin Resource Sharing (CORS).

Error message from Airtable:

TypeError: Failed to fetch
at main on line 27
This issue could be due to CORS settings.

Console error on server side:

OPTIONS /api/webhook 405 Method Not Allowed

Here is my updated Airtable script:

let myTable = base.getTable('Onboarding');
let myView = myTable.getView('Grid view');

let allRecords = await myView.selectRecordsAsync();
let currentRecord = allRecords.records[0];

var payload = {user: 
  {    
   "firstName": currentRecord.getCellValueAsString('firstName'),
   "lastName": currentRecord.getCellValueAsString('lastName'),
   "email": currentRecord.getCellValueAsString('email'),
   },
};

let apiResponse = await fetch('https://example-api.com/api/webhook', {
  method: 'POST',
  body: JSON.stringify(payload),
  headers: {
    "Content-Type": 'application/json',
    "Accept": "application/json",
  }
});

let jsonResponse = await apiResponse.json();

await myTable.updateRecordAsync(currentRecord.id, {
  apiResponse: jsonResponse,
});

My Node.js API’s handler function:

export default async function handler(request, response) {
  const { method } = request;
  console.log("Request method:", method);
  switch (method) {
    case "POST":
      console.log("Request body:", request.body);
      return processRequest(request, response);
    case "OPTIONS":
      response.setHeader("Allow", "POST");
      response.setHeader("Allow", ["GET", "POST", "PUT", "DELETE", "OPTIONS"]);
      response.status(405).end(`Method ${method} Not Allowed`);
      break;
  }
}

const processRequest = async (request, response) => {
   console.log("REQUEST BODY: ", request.body);
   response.status(200).json("Success");
};

What can I do to resolve this CORS problem and ensure my API gets POST requests instead of OPTIONS?

Your OPTIONS handling is the problem. You’re setting conflicting headers and returning 405 instead of 200.

I’ve hit this exact issue before. The browser sends OPTIONS first to check permissions, then your actual POST. Your server needs to approve that preflight request.

Replace your OPTIONS case with this:

case "OPTIONS":
  response.setHeader("Access-Control-Allow-Origin", "*");
  response.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
  response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
  response.status(200).end();
  break;

Also add the CORS header to your POST response:

const processRequest = async (request, response) => {
   response.setHeader("Access-Control-Allow-Origin", "*");
   console.log("REQUEST BODY: ", request.body);
   response.status(200).json("Success");
};

This video explains CORS really well if you want to understand what’s happening:

Once you fix the OPTIONS handling, Airtable will get the green light from the preflight check and send your POST request normally.

Yeah, you’re sending a 405 error for OPTIONS when you need to return 200 with proper CORS headers. Add response.setHeader('Access-Control-Allow-Origin', '*') and response.setHeader('Access-Control-Allow-Headers', 'Content-Type') then do response.status(200).end() instead of the 405. That’ll fix your preflight issue.

Had the exact same headache 6 months ago with a webhook integration. Your OPTIONS handler’s the problem, but there’s more - you’re setting response.setHeader("Allow", "POST") twice with different values. That causes undefined behavior. Just remove both lines, they’re not needed for CORS anyway.

What everyone’s missing: your POST endpoint probably doesn’t have the right CORS headers either. Fix your OPTIONS to return 200 with proper Access-Control headers, then make sure your processRequest function includes response.setHeader('Access-Control-Allow-Origin', '*') before sending the JSON response. Otherwise you’ll hit another CORS error even after the preflight passes.

Your OPTIONS request handling is the problem. When browsers send CORS requests with custom headers, they fire off an OPTIONS preflight request first to check if the actual request is allowed. You’re returning a 405 status instead of handling this properly. Change your OPTIONS case to return a 200 status with the CORS headers: Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers. Once the preflight check passes, the browser will send your POST request.