Using Google Sheets as a webhook receiver: connect Mailchimp, Mailgun, Pipedrive, and more
How to use Google Sheets as a webhook endpoint - receive data from Mailchimp, Mailgun, Pipedrive, Reply.io, and any other service that sends webhooks, straight into a spreadsheet.
Most webhook receivers are servers - Node.js apps, Lambda functions, or third-party middleware that catch an HTTP POST and do something with the payload. That works, but it takes infrastructure to maintain, and often the "something" you need to do is just log the data somewhere your team can see it.
SheetsAPI flips that. Your Google Sheet becomes the receiver. Any service that fires an HTTP POST webhook can be pointed directly at a SheetsAPI endpoint, and the payload lands as a row - no server, no Zapier step in between.
How it works
A SheetsAPI endpoint has this structure:
https://sheetsapi.gkit.mreshank.com/api/spreadsheets/{userKey}/{sheetName}
When you send a POST request with a JSON body, SheetsAPI maps the top-level keys of that
JSON object to column headers in your Sheet. If your Sheet has columns named email,
first_name, and status, and the JSON body contains those same keys, a new row is
appended with the values filled in.
curl -X POST \
"https://sheetsapi.gkit.mreshank.com/api/spreadsheets/abc123/subscribers" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk_your_key_here" \
-d '{"email": "jane@example.com", "first_name": "Jane", "status": "subscribed"}'The response includes the row that was created:
{
"row": {
"email": "jane@example.com",
"first_name": "Jane",
"status": "subscribed"
}
}The mapping is straightforward: JSON key names must match your Sheet's column headers exactly (case-sensitive). Extra keys in the payload that have no matching column are ignored. Missing keys leave that cell empty.
That's the entire model. Once you have the endpoint URL, paste it wherever a service asks for a webhook URL.
Mailchimp → Google Sheets
Mailchimp fires webhook POSTs for list events: subscribe, unsubscribe, profile updates, campaign sends, and more. The most common use is capturing new subscribers into a Sheet for a team that doesn't have Mailchimp access.
Set up your Sheet columns:
| first_name | last_name | status | list_id |
|---|
Mailchimp's subscribe webhook payload (simplified) looks like this:
{
"type": "subscribe",
"data": {
"email": "jane@example.com",
"merges": {
"FNAME": "Jane",
"LNAME": "Doe"
},
"list_id": "abc123"
}
}Mailchimp nests its data, so the keys won't match flat column names directly. The cleanest approach is a thin proxy route - or use Mailchimp's "webhook secret" to send to a small Cloudflare Worker that flattens the payload before forwarding to SheetsAPI. If you prefer zero code, Mailchimp also supports Zapier webhooks as an intermediary step.
For simpler Mailchimp setups using their API + SheetsAPI in sequence, see the form backend use case - the same flattening pattern applies.
Mailgun → Google Sheets
Mailgun sends delivery, bounce, open, click, and spam complaint events as webhook POSTs. Logging these to a Sheet gives your team a plain-English delivery audit trail without needing to interpret Mailgun's dashboard.
Set up your Sheet columns:
| event | recipient | timestamp | message_id | severity |
|---|
Mailgun's webhook payload structure (for a permanent failure):
{
"signature": {
"timestamp": "1719820800",
"token": "...",
"signature": "..."
},
"event-data": {
"event": "failed",
"severity": "permanent",
"recipient": "user@example.com",
"message": { "headers": { "message-id": "msg_001" } },
"timestamp": 1719820800
}
}Again, Mailgun nests its payload under event-data. You have two options:
- A small flattening proxy (a Cloudflare Worker or any serverless function)
that extracts
event-data.*keys and POSTs the flat object to SheetsAPI. - Use Mailgun's "Legacy Webhooks" format (available in the Mailgun dashboard), which sends a form-encoded flat payload - see the limitations note below.
Once flattened, the SheetsAPI POST looks like:
curl -X POST \
"https://sheetsapi.gkit.mreshank.com/api/spreadsheets/abc123/email-events" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk_your_key_here" \
-d '{
"event": "failed",
"severity": "permanent",
"recipient": "user@example.com",
"message_id": "msg_001",
"timestamp": "1719820800"
}'Pipedrive → Google Sheets
Pipedrive fires webhooks on deal events: added, updated, deleted, and the
stage transitions that map to won/lost. Logging deal activity to a Sheet is useful
for weekly reporting, external stakeholders who don't have Pipedrive seats, or
feeding a BI tool that reads from Sheets.
Set up your Sheet columns:
| deal_id | title | status | stage | owner | value | currency | updated_at |
|---|
Pipedrive's webhook body wraps the entity in current and previous keys:
{
"event": "updated.deal",
"current": {
"id": 42,
"title": "Acme Corp - Enterprise",
"status": "won",
"stage_id": 5,
"value": 12000,
"currency": "USD",
"owner_name": "Alex Smith",
"update_time": "2026-07-01 09:00:00"
},
"previous": { "status": "open" }
}Extract current.* in your proxy and map to the Sheet columns above.
This gives you a running log of every deal state change - won, lost, stalled -
without writing a single line of Pipedrive API polling code.
Reply.io and other email sequence tools
Reply.io, Lemlist, Instantly, and similar outreach tools support custom webhook URLs for sequence events: email sent, opened, replied, bounced, opted out.
The pattern is identical: create a Sheet with columns matching the event fields
you care about (contact_email, sequence_name, event, timestamp),
get the SheetsAPI endpoint URL, and paste it into the tool's webhook settings.
Most outreach tools send relatively flat JSON payloads, so the keys often map directly to column headers without a flattening step.
The generic pattern: any service with a webhook URL field
The same approach works for any service that lets you specify a webhook URL:
- Stripe - payment succeeded, subscription cancelled, invoice paid
- GitHub - push events, pull request merged, issue opened
- Typeform - form submission responses
- Cal.com - booking created, cancelled, rescheduled
- Linear - issue created, status changed
- Any internal system that fires HTTP POSTs on events
If the service sends flat JSON where the keys are meaningful, you can point it directly at SheetsAPI without any intermediate layer. If the payload is nested, add one small transformation step.
The point is that you don't need a dedicated server process running continuously to receive these events. SheetsAPI handles the HTTP endpoint; your Sheet stores the data.
See the full list of use cases and the SheetsAPI docs for more integration patterns.
Security: require an API key
By default, a SheetsAPI endpoint is public - any POST request is accepted. For webhook receivers, you want to restrict this so only the sending service can write rows.
Create an API key in the GKit dashboard. Once a key exists,
all your endpoints require a valid Authorization: Bearer sk_... header.
curl -X POST \
"https://sheetsapi.gkit.mreshank.com/api/spreadsheets/abc123/subscribers" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk_live_your_key_here" \
-d '{"email": "jane@example.com", "status": "subscribed"}'Paste the full endpoint URL including the Bearer token wherever the sending service lets you configure a webhook URL with custom headers. Most services (Mailgun, Pipedrive, Reply.io) support custom headers in their webhook settings.
For services that don't support custom headers, you can embed a query param instead - check the SheetsAPI docs for the alternative auth method.
Limitations to know before you build
Non-JSON payloads. Some services send application/x-www-form-urlencoded payloads
(Mailgun's legacy webhook format, some older SMTP event APIs). SheetsAPI expects
application/json. If your sender doesn't support JSON, add a small proxy that parses
the form-encoded body and re-POSTs it as JSON. A Cloudflare Worker does this in
about 15 lines.
Nested JSON. As shown in the Mailchimp and Mailgun sections above, deeply nested payloads won't map to flat Sheet columns. You need to flatten them first.
Webhook retries. Webhook senders retry on non-2xx responses. If SheetsAPI is temporarily unavailable and returns a 5xx, the sender will retry - which means the row could be written multiple times once the endpoint recovers. For idempotency, add a column for a webhook event ID (if the sender includes one) and deduplicate in Sheets using a formula or periodic cleanup.
Rate limits. SheetsAPI writes are throttled by the Google Sheets API limits on the back end. High-volume event streams (thousands of events per minute) are not a good fit for this pattern.
SheetsAPI vs Zapier / Make for this use case
Both approaches can receive a webhook and write a row to Google Sheets. The difference is the architecture:
| SheetsAPI | Zapier / Make | |
|---|---|---|
| Middleware | None - direct HTTP POST to Sheet | Yes - request passes through Zapier/Make servers |
| Cost model | Flat monthly plan, unlimited rows | Per-task pricing; costs scale with volume |
| Setup time | Minutes | Minutes (similar) |
| Payload transformation | Manual (proxy if needed) | Visual editor, easier for non-devs |
| Best for | Developers, high-volume events, cost control | Non-technical teams, complex multi-step flows |
If you need to fan out a webhook to multiple systems - write to Sheets and also send a Slack message and update a CRM record - Zapier or Make are better tools. If you just need the data in a Sheet with minimal cost and no middleware dependency, SheetsAPI is the more direct path.
Getting started
- Sign in at GKit and connect your Google account.
- Open the Google Sheet you want to write rows into.
- Add column headers that match the JSON keys your webhook sender uses.
- Create a SheetsAPI endpoint for that Sheet from the dashboard.
- (Optional) Create an API key and configure it as a Bearer token in the sender.
- Paste the endpoint URL into your service's webhook settings and send a test event.
The row should appear in your Sheet within a second or two of the POST.
Read more about what SheetsAPI can do on the SheetsAPI product page, or browse other use cases to see how teams are using it. If you have questions, the about page has contact information, and there are free utilities at GKit tools for testing endpoints.