Integrate Bolt.new with Google Docs by enabling the Google Docs API in Google Cloud Console, implementing OAuth 2.0 (requires deployment for the redirect URI), then using Next.js API routes to create documents, read document content, and insert text. For simpler read-only display without OAuth, embed documents via iframe. The OAuth callback cannot be tested in Bolt's WebContainer preview — deploy to Netlify or Bolt Cloud first.
Building Google Docs Features into Bolt.new Apps
The Google Docs API gives your Bolt application programmatic access to one of the world's most widely used document platforms. With the API, you can create new documents from templates, read structured document content, perform batch text edits, insert tables and images, and update named ranges — all without users having to download, upload, or copy-paste between systems. This is powerful for automating document generation: contract creation from form data, report generation from database records, proposal drafts populated from CRM data, or meeting notes templates auto-filled from calendar events.
What makes the Google Docs API distinctive is its document model. Unlike a traditional word processor that treats a document as a stream of characters, the Google Docs API represents every document as a structured tree of elements: body, paragraphs, runs, tables, table rows, table cells, images, and named ranges. Reading a document returns a deeply nested JSON object describing every structural element with precise position indices. Editing uses a batch of requests (insert, delete, format) that reference character offsets. This design is powerful but requires some adjustment for developers used to simpler APIs.
For simpler use cases where you only need read-only display of a Google Doc, iframe embedding is far faster to implement than the full API. Any Google Doc with link sharing enabled can be embedded as an iframe in your React components with no OAuth required. The tradeoff is that embedded docs display with Google's formatting — you cannot extract or style the text content. For use cases where you need the actual content (to search, display custom-styled, or process), the full OAuth + API route approach is the right path.
Integration method
Bolt generates the Google Docs integration code — OAuth 2.0 authorization routes, API route handlers for document operations, and React components for document display — through conversation with the AI. Google Docs requires OAuth 2.0, meaning users must authorize your app through Google's consent screen before your app can access their documents. The OAuth callback URL must be a publicly accessible HTTPS address, so the auth flow requires deployment. Document creation and content reads then work through server-side API routes that keep credentials out of the browser.
Prerequisites
- A Google account with access to Google Cloud Console (console.cloud.google.com)
- A Google Cloud project with the Google Docs API enabled
- OAuth 2.0 credentials (Client ID and Client Secret) configured for a web application with your deployed callback URL
- A deployed Bolt.new app on Netlify or Bolt Cloud (required for the OAuth redirect URI — the auth flow cannot run in the WebContainer preview)
- A Next.js project in Bolt with the googleapis npm package (prompt: 'Install the googleapis npm package')
Step-by-step guide
Enable the Google Docs API and create OAuth 2.0 credentials
Enable the Google Docs API and create OAuth 2.0 credentials
Start in the Google Cloud Console. If you do not have a project, create one at console.cloud.google.com by clicking 'New Project'. Give it a name and click Create. With your project selected, navigate to APIs & Services → Library. Search for 'Google Docs API' and click Enable. Also enable the Google Drive API — you will need it to list documents and set sharing permissions on newly created docs. Next, create OAuth 2.0 credentials. Go to APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID. If prompted, configure the OAuth consent screen first: choose 'External' for testing (allows any Google account), fill in the app name, support email, and developer email. On the Scopes page, add https://www.googleapis.com/auth/documents for Docs access and https://www.googleapis.com/auth/drive.file for creating and managing files your app creates. Back in Create Credentials, select 'Web application' as the application type. In Authorized redirect URIs, add your deployed app's callback URL (e.g., https://your-app.netlify.app/api/google/callback) and http://localhost:3000/api/google/callback for local testing. Click Create and copy the Client ID and Client Secret. Store these in your .env.local file. The Google OAuth client secret must only ever appear in server-side code — never in React components or any code that runs in the browser.
Set up Google Docs OAuth 2.0 in my Next.js app. Create a lib/google-auth.ts file that exports a getOAuthClient function returning a configured OAuth2 client using GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URI from environment variables. Create an /api/google/authorize route that generates and redirects to the Google auth URL requesting the docs and drive.file scopes. Create an /api/google/callback route that exchanges the authorization code for tokens, stores them server-side, and redirects to /dashboard on success.
Paste this in Bolt.new chat
1// .env.local2GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com3GOOGLE_CLIENT_SECRET=your_client_secret4GOOGLE_REDIRECT_URI=https://your-app.netlify.app/api/google/callback56// lib/google-auth.ts7import { google } from 'googleapis';89export function getOAuthClient() {10 return new google.auth.OAuth2(11 process.env.GOOGLE_CLIENT_ID,12 process.env.GOOGLE_CLIENT_SECRET,13 process.env.GOOGLE_REDIRECT_URI14 );15}1617export function getAuthUrl(): string {18 const oauth2Client = getOAuthClient();19 return oauth2Client.generateAuthUrl({20 access_type: 'offline',21 scope: [22 'https://www.googleapis.com/auth/documents',23 'https://www.googleapis.com/auth/drive.file',24 ],25 prompt: 'consent', // Force refresh token on each auth26 });27}Pro tip: Set access_type: 'offline' and prompt: 'consent' when generating the auth URL. This ensures Google returns a refresh token on every authorization, not just the first. Without a refresh token, your app will lose access after the one-hour access token expires.
Expected result: Visiting /api/google/authorize redirects to Google's consent screen showing your app name and the requested Docs/Drive scopes. After authorizing, you are redirected back to your callback URL.
Implement the OAuth callback and token storage
Implement the OAuth callback and token storage
After the user authorizes your app on Google's consent screen, Google redirects them to your callback URL with an authorization code as a query parameter. Your callback route must exchange this code for access and refresh tokens by calling Google's token endpoint, then store the tokens so your app can make authenticated Docs API calls. The googleapis library handles the token exchange and management. The OAuth2 client's getToken method exchanges the code for a credentials object containing access_token, refresh_token, expiry_date, and token_type. The refresh_token is particularly important — it allows your server to get new access tokens when the current one expires, without requiring the user to re-authorize. For token storage, the approach depends on your app's complexity. For a single-user tool or prototype, storing tokens in an encrypted HTTP-only cookie is simple and effective. For a multi-user app where different users connect their own Google accounts, store tokens in a database keyed by your user's ID. Never store refresh tokens in environment variables — they must be persisted across server restarts and may need to be updated when refreshed. An important constraint: the OAuth callback is an incoming redirect from Google to your server. Bolt's WebContainer cannot receive these incoming connections. Deploy your app first, register the deployed URL as the redirect URI in Google Cloud Console, and test the full auth flow on the deployed site. During development, you can temporarily use the localhost redirect URI to test the callback locally by running Next.js outside of Bolt.
Create the Google OAuth callback handler at /api/google/callback. It receives the authorization code as a query parameter, exchanges it for tokens using the googleapis OAuth2 client, and stores the access_token, refresh_token, and expiry_date in an encrypted server-side cookie called google_tokens. If there's an error query parameter from Google, redirect to /error with a message. After successful token storage, redirect to /dashboard.
Paste this in Bolt.new chat
1// app/api/google/callback/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { getOAuthClient } from '@/lib/google-auth';45export async function GET(request: NextRequest) {6 const { searchParams } = new URL(request.url);7 const code = searchParams.get('code');8 const error = searchParams.get('error');910 if (error) {11 return NextResponse.redirect(new URL(`/error?message=${encodeURIComponent(error)}`, request.url));12 }1314 if (!code) {15 return NextResponse.json({ error: 'Missing authorization code' }, { status: 400 });16 }1718 try {19 const oauth2Client = getOAuthClient();20 const { tokens } = await oauth2Client.getToken(code);2122 // Store tokens in an HTTP-only cookie23 // In production, encrypt this or store in a database keyed by user ID24 const tokenData = JSON.stringify({25 access_token: tokens.access_token,26 refresh_token: tokens.refresh_token,27 expiry_date: tokens.expiry_date,28 token_type: tokens.token_type,29 });3031 const response = NextResponse.redirect(new URL('/dashboard', request.url));32 response.cookies.set('google_tokens', tokenData, {33 httpOnly: true,34 secure: process.env.NODE_ENV === 'production',35 sameSite: 'lax',36 maxAge: 60 * 60 * 24 * 30, // 30 days37 path: '/',38 });3940 return response;41 } catch (err) {42 console.error('Token exchange error:', err);43 return NextResponse.redirect(new URL('/error?message=auth_failed', request.url));44 }45}4647// lib/get-google-client.ts — helper to get authenticated client from cookie48import { cookies } from 'next/headers';4950export async function getAuthenticatedClient() {51 const cookieStore = cookies();52 const tokenCookie = cookieStore.get('google_tokens');5354 if (!tokenCookie) throw new Error('Not authenticated with Google');5556 const tokens = JSON.parse(tokenCookie.value);57 const oauth2Client = getOAuthClient();58 oauth2Client.setCredentials(tokens);59 return oauth2Client;60}Pro tip: The googleapis library automatically refreshes the access token using the refresh token when it is about to expire, as long as you call oauth2Client.setCredentials(tokens) before making API calls. Update the stored tokens after each API call if the credentials changed (check oauth2Client.credentials after the call).
Expected result: After completing the OAuth flow on the deployed app, the browser redirects to /dashboard and a google_tokens HTTP-only cookie is set. Subsequent API routes can retrieve this cookie to make authenticated Docs API calls.
Create and read Google Docs via API routes
Create and read Google Docs via API routes
With authentication working, you can now create documents and read their content. The Google Docs API is accessed through the googleapis library's google.docs() client. Every API call needs an authenticated OAuth2 client — use the getAuthenticatedClient helper from the previous step. Creating a document is a single API call to documents.create with a title. This creates an empty document and returns the document ID, which is the same string you see in the Google Docs URL (/document/d/{documentId}/edit). Once you have a document ID, you can insert content using batchUpdate, set permissions using the Drive API, and get the document URL. Reading document content uses documents.get, which returns the complete document structure as a nested JSON object. The body.content array contains structural elements: paragraphs, tables, and section breaks. Each paragraph has a paragraphStyle indicating whether it is a heading, normal text, title, or list item. Within paragraphs, textRun elements contain the actual text content and formatting (bold, italic, foreground color, font size). Parsing this structure to display as HTML requires traversing the element tree and mapping structural types to HTML elements. For the most common use case — replacing template placeholders with dynamic values — use batchUpdate with replaceAllText requests. This is far simpler than calculating character offsets. Define placeholder strings in your template document (e.g., {{CLIENT_NAME}}, {{DATE}}, {{PROJECT_SCOPE}}) and batch-replace them all with actual values in a single API call.
Create two API routes for Google Docs operations. First: /api/docs/create that accepts a POST with title, and optional templateId (if provided, copy that document using Drive API instead of creating empty). Return the new documentId and editUrl. Second: /api/docs/[docId]/content that reads the document using the Docs API and returns a simplified parsed structure: an array of content blocks each with type (heading1-6, paragraph, listItem, table) and text content. Use the authenticated OAuth client from the cookie.
Paste this in Bolt.new chat
1// app/api/docs/create/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { google } from 'googleapis';4import { getAuthenticatedClient } from '@/lib/get-google-client';56export async function POST(request: NextRequest) {7 try {8 const { title, templateId } = await request.json() as {9 title: string;10 templateId?: string;11 };1213 const auth = await getAuthenticatedClient();1415 if (templateId) {16 // Copy an existing template document17 const drive = google.drive({ version: 'v3', auth });18 const copy = await drive.files.copy({19 fileId: templateId,20 requestBody: { name: title },21 });22 const docId = copy.data.id!;23 return NextResponse.json({24 documentId: docId,25 editUrl: `https://docs.google.com/document/d/${docId}/edit`,26 viewUrl: `https://docs.google.com/document/d/${docId}/view`,27 });28 }2930 // Create a new empty document31 const docs = google.docs({ version: 'v1', auth });32 const doc = await docs.documents.create({33 requestBody: { title },34 });3536 const docId = doc.data.documentId!;37 return NextResponse.json({38 documentId: docId,39 editUrl: `https://docs.google.com/document/d/${docId}/edit`,40 viewUrl: `https://docs.google.com/document/d/${docId}/view`,41 });42 } catch (error) {43 const message = error instanceof Error ? error.message : 'Failed to create document';44 return NextResponse.json({ error: message }, { status: 500 });45 }46}4748// Replace template placeholders in a document49// app/api/docs/[docId]/replace/route.ts50export async function POST_replace(request: NextRequest, { params }: { params: { docId: string } }) {51 const { replacements } = await request.json() as {52 replacements: Record<string, string>;53 };54 const auth = await getAuthenticatedClient();55 const docs = google.docs({ version: 'v1', auth });5657 const requests = Object.entries(replacements).map(([placeholder, value]) => ({58 replaceAllText: {59 containsText: { text: `{{${placeholder}}}`, matchCase: true },60 replaceText: value,61 },62 }));6364 await docs.documents.batchUpdate({65 documentId: params.docId,66 requestBody: { requests },67 });6869 return NextResponse.json({ success: true });70}Pro tip: When creating documents from templates, copy the template using the Drive API (files.copy) rather than creating a blank document and inserting content. Copying preserves all formatting, styles, headers, footers, and images from the template — you just replace the placeholder text.
Expected result: POST /api/docs/create with { title: 'Q4 Report' } creates a new Google Doc and returns its documentId and edit URL. Opening the edit URL in a browser shows the document in Google Docs.
Embed Google Docs for simple read-only display
Embed Google Docs for simple read-only display
For use cases where you only need to display a Google Doc within your app without editing or extracting content, iframe embedding is much simpler than the full OAuth integration. Google provides a dedicated embed URL for any document that has been shared with 'Anyone with the link can view' permissions. The embed URL format is: https://docs.google.com/document/d/{documentId}/pub?embedded=true. This renders the document content as clean HTML in an iframe, with Google's styling applied. No API key, no OAuth, no server-side code needed — just a React component with an iframe element. This approach is perfect for: public documentation pages, help articles maintained in Google Docs, terms of service or privacy policy pages, or any content where your team writes in Google Docs and wants to display it live in your app without a CMS migration. When someone updates the doc in Google Docs, the embedded version updates automatically. The limitations are meaningful: you cannot extract or restyle the text content, the iframe shows Google's formatting (which you cannot override), and the embed URL only works for publicly shared documents. Private documents require the OAuth flow. Also note that the embedded Docs view does not render the document's print layout — it renders a web-optimized version that may look slightly different from the editor view. For Bolt's WebContainer preview, iframe embedding works without any configuration — just drop in the embed URL. This is the fastest way to get Google Docs content into a Bolt app during initial prototyping.
Create a GoogleDocEmbed React component that accepts a documentId prop and renders the document as an iframe using the Google Docs publish embed URL. Add a loading state shown while the iframe loads. Add an 'Open in Google Docs' button that links to the full document editor URL. Style the iframe to take the full width of its container with a minimum height of 600px. Include a note in the component JSDoc that the document must have 'Anyone with the link can view' sharing enabled.
Paste this in Bolt.new chat
1// components/GoogleDocEmbed.tsx2'use client';34import { useState } from 'react';56interface GoogleDocEmbedProps {7 /** Google Document ID from the URL: /document/d/{documentId}/edit */8 documentId: string;9 /** Minimum height of the embed iframe in pixels. Default: 600 */10 minHeight?: number;11 /** Show the 'Open in Google Docs' link. Default: true */12 showOpenLink?: boolean;13}1415/**16 * Embeds a Google Doc as an iframe using the published embed URL.17 * REQUIREMENT: The document must have sharing set to18 * 'Anyone with the link can view' in Google Docs share settings.19 */20export default function GoogleDocEmbed({21 documentId,22 minHeight = 600,23 showOpenLink = true,24}: GoogleDocEmbedProps) {25 const [isLoaded, setIsLoaded] = useState(false);2627 const embedUrl = `https://docs.google.com/document/d/${documentId}/pub?embedded=true`;28 const editUrl = `https://docs.google.com/document/d/${documentId}/edit`;2930 return (31 <div className="relative w-full border rounded-lg overflow-hidden">32 {!isLoaded && (33 <div34 className="absolute inset-0 flex items-center justify-center bg-gray-50"35 style={{ minHeight: `${minHeight}px` }}36 >37 <div className="text-gray-400 flex flex-col items-center gap-2">38 <div className="w-8 h-8 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin" />39 <p className="text-sm">Loading document...</p>40 </div>41 </div>42 )}43 <iframe44 src={embedUrl}45 onLoad={() => setIsLoaded(true)}46 style={{ minHeight: `${minHeight}px`, width: '100%', border: 'none' }}47 title="Google Document"48 sandbox="allow-scripts allow-same-origin"49 />50 {showOpenLink && (51 <div className="border-t px-4 py-2 bg-gray-50 flex justify-end">52 <a53 href={editUrl}54 target="_blank"55 rel="noopener noreferrer"56 className="text-sm text-blue-600 hover:underline"57 >58 Open in Google Docs →59 </a>60 </div>61 )}62 </div>63 );64}Pro tip: If the iframe shows 'Sorry, unable to open the file at this time' or a Google login page, the document's sharing settings are still set to private. In Google Docs, click Share → Change to Anyone with the link → Viewer → Done. The embed URL only works for viewer-accessible documents.
Expected result: The GoogleDocEmbed component renders the Google Doc content inside your Bolt app. The document text, headings, and formatting are visible in the preview, and the 'Open in Google Docs' link opens the document editor in a new tab.
Common use cases
Contract generation from form data
When a user fills out a form in your Bolt app (client name, project scope, pricing), automatically create a Google Doc using a template document, replacing placeholder text with the actual values. The generated document is ready to share with the client for signing.
Create a Next.js app that generates Google Docs contracts. When a user submits a form with client name, project description, and total price, call the Google Docs API to create a new document based on a template document ID. Replace the placeholders {{CLIENT_NAME}}, {{PROJECT_DESCRIPTION}}, and {{TOTAL_PRICE}} using the Docs API batchUpdate with replaceAllText requests. Return the new document's URL.
Copy this prompt to try it in Bolt.new
Document content viewer with custom styling
Build a custom document viewer that fetches a Google Doc's content through the API and renders it with your app's own typography and styling, rather than showing it in Google's iframe. This is useful for help centers, knowledge bases, or content platforms where you want consistent branding.
Build a Google Docs content viewer that fetches document content using the Google Docs API and renders it as styled React components. Parse the document's body elements — paragraphs, headings (HEADING_1 through HEADING_6), and lists — and render them with Tailwind CSS typography classes. Handle bold, italic, and link formatting within text runs.
Copy this prompt to try it in Bolt.new
Meeting notes auto-population
Before a meeting, automatically create a Google Doc with the meeting agenda, attendee list, and action items sections pre-populated from your app's meeting data. The document link is sent to participants so everyone has a structured notes template ready when the meeting starts.
Create an API route that creates a meeting notes Google Doc. Accept meeting title, date, attendees array, and agenda items array as input. Create a new Google Doc with the title as the document title, a formatted attendees list as a table, agenda items as a numbered list, and empty sections for Notes and Action Items. Return the document ID and shareable link.
Copy this prompt to try it in Bolt.new
Troubleshooting
OAuth callback returns 'redirect_uri_mismatch' error from Google
Cause: The redirect URI sent in the authorization request does not exactly match one of the URIs registered in Google Cloud Console. Google performs a strict string comparison including protocol, path, and trailing slashes.
Solution: In Google Cloud Console → APIs & Services → Credentials, open your OAuth 2.0 Client ID and verify the Authorized redirect URIs. Copy one exactly (character for character) into your GOOGLE_REDIRECT_URI environment variable. Common mismatches: http vs https, missing or extra trailing slash, localhost vs 127.0.0.1.
OAuth callback works but subsequent API calls return 401 after one hour
Cause: The access token has expired and the refresh token is not being used to obtain a new one. This happens when access_type was not set to 'offline' in the auth URL, so no refresh token was issued.
Solution: Regenerate the auth URL with access_type: 'offline' and prompt: 'consent'. The user must re-authorize to receive a new refresh token. After that, the googleapis library automatically uses the refresh token when the access token expires, provided you call oauth2Client.setCredentials(tokens) before each API call.
1// Correct auth URL generation:2const authUrl = oauth2Client.generateAuthUrl({3 access_type: 'offline', // Required for refresh token4 prompt: 'consent', // Forces new refresh token even if already authorized5 scope: ['https://www.googleapis.com/auth/documents'],6});Google OAuth login page appears in the Bolt preview but after authorizing, the page shows a 404 or no redirect happens
Cause: The OAuth callback URL points to your deployed app's domain, but the Bolt preview runs on a different URL. Intuit redirects to the registered domain, which the WebContainer cannot intercept.
Solution: Deploy your app to Netlify or Bolt Cloud first. Register the deployed HTTPS URL (e.g., https://your-app.netlify.app/api/google/callback) in Google Cloud Console. Test the complete auth flow on the deployed site. For local development outside Bolt, add http://localhost:3000/api/google/callback as a second authorized redirect URI.
documents.get returns document structure but extracting text is unclear — nested objects everywhere
Cause: The Google Docs API document model is a deeply nested tree of structural elements, not a flat text string. Each paragraph contains elements, each element may be a textRun with content or an inlineObjectElement for images.
Solution: Use a recursive extraction pattern that traverses the body.content array and extracts text from nested textRun.content fields. The helper function below extracts all text content as a plain string from any Google Docs API response.
1function extractText(doc: GoogleDocsDocument): string {2 const lines: string[] = [];3 for (const element of doc.body?.content ?? []) {4 if (element.paragraph) {5 const text = element.paragraph.elements6 ?.map((el) => el.textRun?.content ?? '')7 .join('');8 if (text?.trim()) lines.push(text.replace(/\n$/, ''));9 }10 }11 return lines.join('\n');12}Best practices
- Always deploy before testing the OAuth flow — Google's redirect URI must be a publicly accessible HTTPS URL that the WebContainer cannot provide during development.
- Store OAuth tokens in a database for multi-user apps, not in cookies or environment variables. Tokens expire and refresh, so they need to be updatable at runtime.
- Use replaceAllText in batchUpdate for template-based document generation — it is simpler and more reliable than calculating character offsets for insertions.
- Request only the scopes your app needs: use https://www.googleapis.com/auth/documents for Docs access and https://www.googleapis.com/auth/drive.file to only access files your app creates, rather than the broader drive scope.
- For read-only public content display, prefer iframe embedding over the API — it requires no authentication setup and automatically reflects document updates.
- Cache read API responses when document content does not change frequently. The Docs API has per-user rate limits; excessive reads on the same documents can trigger 429 errors.
- Never expose your Google Client Secret or OAuth tokens in client-side React code. All Google API calls must go through server-side Next.js API routes.
Alternatives
Notion's block-based API is easier to parse programmatically than Google Docs' structural element tree, and Notion is better for building custom databases and knowledge bases alongside documents.
Confluence is the right choice for teams already using Jira and the Atlassian ecosystem, with a REST API that supports structured page content in enterprise environments.
Quip (by Salesforce) offers a simpler API for document and spreadsheet manipulation and is a natural fit for teams already using Salesforce CRM.
Frequently asked questions
Can I test the Google Docs API in Bolt's preview without deploying?
Partially. Outbound API calls to read and create documents work fine once you have a valid access token. However, the OAuth flow — where Google redirects back to your app after authorization — requires a deployed URL that the WebContainer cannot provide. Deploy first to complete auth, obtain tokens, then you can use those tokens to test most API operations in development.
Does Bolt.new have a native Google Docs integration?
No. Google Docs is not one of Bolt's native connectors. You implement the integration using Next.js API routes and the googleapis npm package. Bolt's AI generates most of the boilerplate code when prompted, but you need to set up credentials in Google Cloud Console manually.
How do I allow users to connect their own Google account rather than using my service account?
The OAuth 2.0 flow described in this guide is exactly the mechanism for user-specific access. Each user who authorizes your app receives their own tokens, stored in your database associated with their user ID. When they make requests, use their stored tokens to call the Docs API on their behalf. This is the standard pattern for apps that read or edit documents in users' own Google Drive.
What is the Google Docs API rate limit?
The Google Docs API allows 300 read requests per minute and 60 write requests per minute per user. For a dashboard accessed by many users simultaneously, each user has their own quota so the total throughput scales. If you hit rate limits (429 error), implement exponential backoff retry logic. Caching frequently-read documents is the most effective way to stay within limits.
Can I insert images into Google Docs through the API?
Yes, using the insertInlineImage request in batchUpdate. You provide a URI pointing to a publicly accessible image (Google fetches it from the URL you provide) and the insertion location index within the document. The image must be accessible over HTTPS without authentication — you cannot reference private S3 or local file system paths.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation