Getting 401 unauthorized error when making external API calls from Google Workspace add-on dialog

I’m building a Google Workspace extension that works with Docs, Sheets, and Slides. I have a custom dialog box where users can input some information, and then I need to call an external REST API to fetch data (specifically an SVG graphic).

The issue I’m facing is that whenever I try to make the API request from within the dialog, I keep getting a 401 Unauthorized error.

Here’s my fetch request code:

var userInput = {"type":"diagram"};
var token = "my-secret-token";
var requestData = {
   'diagram': diagramCode,
   'token': token,
   'style':'dark'
};
const result = await fetch('https://api.example.com:8080', {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(requestData) 
    });

When I test the exact same API call using curl from my terminal, it works perfectly:

curl -d '{"diagram":"flowchart TD\nStart --> Decision\nDecision -->|Yes| End1\nDecision -->|No| End2","token":"[TOKEN]","style":"dark"}' -H "Content-Type: application/json" -X POST https://api.example.com:8080/generate

I’ve searched online but most answers focus on making requests TO Google Apps Script, not FROM it to external services.

I don’t want to use the server-side URLFetch because it would freeze the UI, and it seems wrong to handle client-side dialog data on the server.

Are there specific security limitations for external API calls from Google add-ons?

That 401 error isn’t from your server - Google’s security sandbox is blocking the request before it even gets there. Workspace add-ons run in a restricted iframe that won’t let client-side JavaScript make direct external API calls.

I hit this same issue last year building something similar. The fix isn’t elegant but it works: move your fetch call to Apps Script backend using UrlFetchApp.fetch(), then call it from your dialog with google.script.run. Yeah, there’s latency, but throw up a spinner and use the success/failure handlers right and you’ll be fine.

This isn’t regular CORS - it’s Google deliberately locking things down. Your curl works because it’s not in Google’s sandbox. You’ve got to move API calls server-side if you want to play by Google’s rules.

Had the same issue building my first workspace extension. That 401 isn’t from your external API - Google’s CSP blocks cross-origin requests inside their iframe. Your fetch never even reaches the server. I fixed it by running the API call through a Google Apps Script web app set to ‘executable by anyone.’ Deploy your script as a web app, then point your dialog at that URL instead of hitting your external service directly. The web app proxies the request and can call external APIs freely with UrlFetchApp. Keeps everything async like you want while staying inside Google’s security box. Just validate inputs properly since that web app endpoint will be public.

Same issue here. Google’s sandbox blocks external API calls from the client side - CORS and security policies.

Why fight Google’s limitations though? Route everything through an automation platform instead. I fixed this by using Latenode as middleware.

Set up a webhook endpoint in Latenode that takes your dialog data, calls your external service, and returns the SVG. Your Google add-on just hits the Latenode webhook instead of the external API.

This bypasses CORS restrictions, keeps everything async so your UI stays smooth, and gives you proper error handling and logging. You can add data transformation, caching, or rate limiting without touching your add-on code.

I’ve done this for multiple external services with Google Workspace apps. Works great and scales much better than fighting Google’s sandbox.

yea, external api calls from client side in gws add-ons r limited for sec. try handling that fetch in the server-side script. use html service to link your dialog with backend instead.

Everyone’s explaining the sandbox issue but missing the bigger picture. Why work around Google’s restrictions when you can step outside their box entirely?

Forget server-side patches or web app proxies. Build a proper API pipeline outside Google’s environment. I deal with this exact scenario all the time by creating dedicated automation workflows that handle requests independently.

Here’s how: Set up Latenode to receive your dialog data via webhook. It grabs data from your external API, processes the SVG response, and shoots it back to your add-on. No CORS headaches since nothing runs in Google’s sandbox.

The real win? You get proper request queuing, error recovery, and response caching without bloating your add-on code. Your dialog stays snappy, users get faster responses on repeat requests, and you can tweak API integration without republishing.

I’ve used this pattern for several enterprise tools. Way cleaner than battling Google’s security model or complicating your Apps Script backend.

You’re encountering CORS restrictions due to Google’s sandboxed environment for Workspace add-ons. External APIs typically don’t whitelist Google’s iframe domains, which triggers those 401 errors.

I’ve faced a similar issue while developing an extension. A solution is to create a bridge function in your Apps Script server-side code to manage the API call. Then, utilize google.script.run from your dialog. Although there may be some latency, incorporating a loading indicator can help keep the UI responsive.

Shifting your fetch logic to a server-side function and invoking it with google.script.run.withSuccessHandler() from your dialog can enhance the user experience while adhering to Google’s security limitations.

check if ur external api wants the authorization header instead of putting the token in the body. some apis expect Authorization: Bearer your-token in the headers, not in the json payload. add that header to your fetch request and see if it fixes the 401.