ProductsDocsBlogConsultingAboutContactGet Started
Back to BlogA Magento 2 checkout firing a webhook that appends the new order as a row in a Google Sheet seconds later, with the Apps Script endpoint in between
8 min readMageSheet Team

Magento to Google Sheets Integration: Real-Time Order Sync with Apps Script

Magento 2Adobe CommerceGoogle SheetsApps ScriptIntegrationsAutomationE-commerce

You can connect Magento 2 (Adobe Commerce) to Google Sheets in an afternoon, with no middleware and no monthly connector fee. Magento fires a webhook the moment an order is placed; a Google Apps Script web app catches it and appends the row. Orders appear in the sheet within about a second of checkout, and the whole pipeline runs inside your own Google account.

This guide builds that end to end — the Apps Script side, the Magento side, and the one production mistake that will slow your checkout down if you ship the simple version of it.

Most teams reach this page after doing it the two expensive ways: manual CSV exports at the end of each day, or a middleware connector charging a monthly fee to poll an API and still arrive minutes late. Webhooks remove both. If you need a different slice of the same integration — products, prices, or pushing data back into Magento — there is a map at the end of this post.

The Google Workspace half of this builds on patterns we cover separately: building interfaces on Sheets, handling auth, and receiving webhooks in Apps Script.

The Architecture: Magento Webhook to Apps Script

We are going to use the doPost() architecture we discussed in our Apps Script Webhooks guide. The flow is simple:

  1. A customer clicks "Place Order" in Magento.
  2. Magento fires an internal event (sales_order_save_commit_after).
  3. Your Magento server sends a lightweight HTTP POST payload directly to a Google Apps Script URL.
  4. Google Apps Script parses the JSON and appends the order to a Google Sheet.

Notice what is missing here? No middleware. No API polling. No delays. It happens instantly.

Step 1: Prepping the Google Sheet Backend

First, let's create the destination. Open a new Google Sheet, name the first tab LiveOrders, and create the following headers in Row 1:

Magento Order IDStatusGrand TotalCustomer Email

Next, open Extensions > Apps Script and deploy the listener. We'll add some basic security here to ensure random bots don't fill your sheet with garbage data.

// Code.gs

// Define a secret key that only Magento and Apps Script know
const WEBHOOK_SECRET = "MageSheet_Super_Secret_2026"; 

function doPost(e) {
  try {
    const payload = JSON.parse(e.postData.contents);
    
    // Security check! Did Magento send the correct secret?
    if (payload.secret !== WEBHOOK_SECRET) {
      return ContentService.createTextOutput("Forbidden: Invalid Secret").setStatusCode(403);
    }
    
    // Parse the order data
    const order = payload.order_data;
    
    // Select the sheet and append
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("LiveOrders");
    sheet.appendRow([
      order.increment_id,
      order.status,
      order.grand_total,
      order.customer_email
    ]);
    
    return ContentService.createTextOutput(JSON.stringify({"status": "success"}))
      .setMimeType(ContentService.MimeType.JSON);
      
  } catch (error) {
    return ContentService.createTextOutput("Error: " + error.message).setStatusCode(500);
  }
}

Deploy this as a Web App (Execute as: Me, Access: Anyone). Copy the resulting URL.

Step 2: Firing the Event from Magento 2

While Magento 2 has robust REST and GraphQL APIs for pulling data, native "Outbound Webhooks" often require a small custom module.

To keep this tutorial lightweight, we will create a simple Observer module in Magento. Let's assume you have a custom module named MageSheet_GoogleSync.

You need an events.xml file in app/code/MageSheet/GoogleSync/etc/events.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_place_after">
        <observer name="magesheet_sync_order_to_google" instance="MageSheet\GoogleSync\Observer\SyncOrder" />
    </event>
</config>

And your Observer file app/code/MageSheet/GoogleSync/Observer/SyncOrder.php:

<?php
namespace MageSheet\GoogleSync\Observer;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\HTTP\Client\Curl;

class SyncOrder implements ObserverInterface
{
    private $curl;

    public function __construct(Curl $curl)
    {
        $this->curl = $curl;
    }

    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $order = $observer->getEvent()->getOrder();
        
        // The URL you got from Google Apps Script
        $googleAppUrl = "https://script.google.com/macros/s/AKfycby..._aBCD/exec";
        
        // Prepare payload matching our Apps Script expectations
        $payload = [
            "secret" => "MageSheet_Super_Secret_2026",
            "order_data" => [
                "increment_id" => $order->getIncrementId(),
                "status" => $order->getStatus(),
                "grand_total" => $order->getGrandTotal(),
                "customer_email" => $order->getCustomerEmail()
            ]
        ];

        // Fire the cURL POST request
        $this->curl->addHeader("Content-Type", "application/json");
        $this->curl->post($googleAppUrl, json_encode($payload));
        
        // Important: Use asynchronous queues (RabbitMQ/Cron) in production
        // to prevent Google's response time from slowing down Magento checkout!
    }
}

The Crucial Production Warning: Asynchronous Queues

In the PHP example above, we placed a direct cURL request inside the sales_order_place_after event.

Warning: While this is great for a development test, doing this synchronously in production means your customer's browser will keep spinning on the checkout page until Google Apps Script replies. If Google is slow, your checkout is slow.

For enterprise environments, you MUST wrap that cURL request in a Magento Message Queue (RabbitMQ) or process it via a Cron job. This ensures that Magento instantly tells the customer "Order Success", while the actual HTTP request to Google happens a few seconds later in the background.

The Power of Instant Visibility

By utilizing this architecture, your Ops team can open Google Sheets and watch orders arrive the moment a B2B transaction completes. From there, you can use Sheets formulas, Looker Studio dashboards, or Google Apps Script triggers to automatically forward that order to a 3PL or accounting department.

Backfilling the Orders You Already Have

Webhooks only deliver what happens next. On day one your sheet is empty, and most teams want their existing orders in there too. That is a separate job, done once, against Magento's REST API rather than the webhook.

Create an integration in the Magento admin (System → Extensions → Integrations), grant it read access to Sales, activate it, and copy the access token. Then page through the orders endpoint:

function backfillOrders() {
  const props = PropertiesService.getScriptProperties();
  const base  = props.getProperty('MAGENTO_BASE');   // https://yourstore.com
  const token = props.getProperty('MAGENTO_TOKEN');
  const since = props.getProperty('BACKFILL_CURSOR') || '2026-01-01 00:00:00';

  const rows = [];
  let page = 1;

  while (page <= 40) {                                // hard stop, inside 6 minutes
    const q = [
      'searchCriteria[filter_groups][0][filters][0][field]=created_at',
      'searchCriteria[filter_groups][0][filters][0][value]=' + encodeURIComponent(since),
      'searchCriteria[filter_groups][0][filters][0][condition_type]=gt',
      'searchCriteria[sortOrders][0][field]=created_at',
      'searchCriteria[sortOrders][0][direction]=ASC',
      'searchCriteria[pageSize]=100',
      'searchCriteria[currentPage]=' + page,
    ].join('&');

    const res = UrlFetchApp.fetch(base + '/rest/V1/orders?' + q, {
      headers: { Authorization: 'Bearer ' + token },
      muteHttpExceptions: true,
    });
    if (res.getResponseCode() !== 200) {
      throw new Error('Magento ' + res.getResponseCode() + ': ' +
                      res.getContentText().slice(0, 300));
    }

    const items = JSON.parse(res.getContentText()).items || [];
    items.forEach(o => rows.push([
      o.increment_id, o.created_at, o.status,
      o.customer_email, Number(o.grand_total), o.order_currency_code,
    ]));

    if (items.length < 100) break;
    page++;
  }

  if (!rows.length) return;
  const sheet = SpreadsheetApp.getActive().getSheetByName('Orders');
  sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length).setValues(rows);
  props.setProperty('BACKFILL_CURSOR', rows[rows.length - 1][1]);   // resume point
}

Three details make this survive a real catalogue. Store a cursor so a run that hits the six-minute ceiling resumes instead of restarting — see running long jobs past that limit. Write once with setValues, never appendRow in a loop. And grant read-only access: a reporting integration has no business holding write permissions on your store.

Run it once to catch up, then let the webhook keep the sheet current from there.

Which Part of the Integration Do You Need?

"Magento to Google Sheets" covers several different jobs. This post is the order-sync one. Here is where the others live:

What you wantDirectionWhere it is covered
New orders arriving liveMagento → SheetsThis post
Historical ordersMagento → SheetsThe backfill section above
Line items for fulfilmentMagento → SheetsOrder line item exports
Products, prices, catalogueMagento → SheetsOmnichannel PIM with Google Workspace
Customers and companiesMagento → SheetsA free B2B CRM in Google Workspace
Bulk updates back into MagentoSheets → MagentoTriggering Magento REST APIs from Sheets
Invoices from synced ordersSheets → Docs/PDFAutomated B2B invoices
Dashboards on the synced dataSheets → BILooker Studio vs Apps Script dashboards

If you want the whole picture rather than one piece, the complete Magento and Google Sheets automation playbook puts these in order.

What This Replaces

A middleware connector for Magento-to-Sheets runs from roughly $30 to well over $100 a month, polls on a schedule so your data is always a few minutes stale, and meters by task, so the bill grows with your order volume. The pipeline above costs nothing to run, arrives in about a second, and lives in your own Google account against your own integration token. We do the same arithmetic across automation platforms generally in rent or own your automation.

The parts that get genuinely hard are not in the code above: message queues so checkout never waits on Google, HMAC verification on the webhook endpoint, replay of dropped events, and staying inside Apps Script's quotas at volume. If you would rather have that built once and handed over working against your real order volume, tell us what your store needs to see in the sheet. The feasibility review is free — and if a connector genuinely covers your case for less than the build, we will say so.

Frequently Asked Questions

Why use webhooks instead of Magento's REST API polling for order sync?

Polling wastes cycles and adds latency. If you poll every 5 minutes, the average order takes 2.5 minutes to show up in your Sheet. Webhooks deliver in under 500ms because Magento pushes the event the instant it fires. You also eliminate API rate-limit risk — polling 12 times an hour across multiple endpoints burns through Magento's default throttle on busy stores. Webhooks are the correct production pattern.

What happens when Google Apps Script is down or returns an error?

Magento's webhook subscriber will retry with exponential backoff, but only for a limited number of attempts before dropping the event. The production-safe pattern is to have your Apps Script endpoint respond 200 OK immediately after appending to a raw intake sheet, then process the event in a background trigger. This decouples delivery from processing — if your enrichment logic breaks, you still have the raw event and can replay it later.

Does this work on Magento Open Source, Adobe Commerce Cloud, and on-prem equally?

Yes for all three, with small config differences. Open Source and on-prem need a custom module to emit the webhook (or an extension like Mageplaza Webhook). Adobe Commerce Cloud supports webhooks natively via the Extensibility framework in 2.4.6+. The Apps Script endpoint is identical in all three cases — what changes is only the Magento-side wiring.

How do I protect the webhook endpoint from being called by anyone with the URL?

Two layers: (1) Magento signs the webhook payload with an HMAC shared secret, and your doPost() function rejects any request whose signature does not match. (2) The Apps Script deployment uses 'Execute as: Me' plus a URL path secret so the endpoint is non-guessable. Never rely on the URL being secret alone — scanners find Apps Script webhook URLs within hours of deployment.

Is there a limit to how many orders per hour this pipeline can handle?

Apps Script has a 6-minute execution ceiling and roughly 90-second quota per individual URL fetch. In practice the sync handles 2,000-3,000 orders per hour comfortably. Past that, the pattern is to have the webhook write raw events into Google Sheets only, and then pull them into BigQuery on a schedule for heavy analytics — we cover that handoff in our Looker Studio article.

Stay Updated

Get the latest insights on AI, e-commerce, and Magento delivered to your inbox.