← Back to invitation

RSVP → Google Sheets setup

Integration status

Checking…

  1. 1. Create the script

    Go to script.google.com → New project. Replace the contents of Code.gs with the code below and save.

  2. 2. Authorize & create the spreadsheet

    Choose the setup function in the toolbar and click Run. Approve the Google permissions (Advanced → Go to project if warned). This automatically creates the “Haider & Munira — RSVPs” spreadsheet in your Drive. Open the Execution log to see its link and a TOKEN.

  3. 3. Deploy as web app

    Deploy → New deployment → type Web app. Execute as: Me. Who has access: Anyone. Deploy and copy the URL ending in /exec.

  4. 4. Configure the endpoint

    In chat, ask to add the secrets RSVP_ENDPOINT_URL (the /exec URL) and RSVP_SHARED_SECRET (the TOKEN from the log). They are stored privately on the server — never shown on the page.

  5. 5. Test

    Click “Send test RSVP” above. Only once it succeeds is the integration live. You can delete the TEST row afterwards.

Apps Script code

// Haider & Munira — RSVP receiver (Google Apps Script)
const SHEET_NAME = 'RSVPs';
const HEADERS = ['Timestamp','Guest Name','Guest Count','Attendance','Events','Message','Source'];

// Run this ONCE from the editor: creates the spreadsheet and stores its ID.
function setup() {
  const props = PropertiesService.getScriptProperties();
  let id = props.getProperty('SHEET_ID');
  if (!id) {
    const ss = SpreadsheetApp.create('Haider & Munira — RSVPs');
    id = ss.getId();
    props.setProperty('SHEET_ID', id);
    const sh = ss.getSheets()[0];
    sh.setName(SHEET_NAME);
    sh.appendRow(HEADERS);
    sh.setFrozenRows(1);
    sh.getRange(1,1,1,HEADERS.length).setFontWeight('bold').setBackground('#0b4d3b').setFontColor('#f5e6b8');
  }
  // Optional shared secret — copy this value into the website's RSVP_SHARED_SECRET.
  if (!props.getProperty('TOKEN')) props.setProperty('TOKEN', Utilities.getUuid());
  Logger.log('Spreadsheet: https://docs.google.com/spreadsheets/d/' + id);
  Logger.log('TOKEN: ' + props.getProperty('TOKEN'));
}

// Health check: open the /exec URL in a browser to confirm deployment.
function doGet() {
  return json({ ok: true, service: 'haider-munira-rsvp' });
}

function validate(b) {
  const name = String(b.name || '').trim();
  if (name.length < 2 || name.length > 100) return 'invalid name';
  if (b.attendance !== 'attending' && b.attendance !== 'declined') return 'invalid attendance';
  const guests = Number(b.guests);
  const events = Array.isArray(b.events) ? b.events.filter(function (x) { return x === 'mehendi' || x === 'reception'; }) : [];
  if (b.attendance === 'attending') {
    if (!(guests >= 1 && guests <= 20 && Math.floor(guests) === guests)) return 'invalid guest count';
    if (!events.length) return 'select at least one event';
  }
  b.name = name; b.events = events;
  b.guests = b.attendance === 'attending' ? guests : 0;
  b.message = String(b.message || '').slice(0, 500);
  return null;
}

// Requests are sent as text/plain (a CORS "simple request"), so browsers need no
// preflight; Apps Script web apps automatically allow cross-origin reads of the JSON.
function doPost(e) {
  try {
    const props = PropertiesService.getScriptProperties();
    const body = JSON.parse(e.postData.contents);
    const token = props.getProperty('TOKEN');
    if (token && body.token !== token) return json({ ok: false, error: 'unauthorized' });
    const err = validate(body);
    if (err) return json({ ok: false, error: err });
    const ss = SpreadsheetApp.openById(props.getProperty('SHEET_ID'));
    const sh = ss.getSheetByName(SHEET_NAME);
    const lock = LockService.getScriptLock(); lock.waitLock(10000);
    try {
      sh.appendRow([new Date(), body.name, body.guests, body.attendance,
        body.events.join(', '), body.message, body.test ? 'TEST' : 'website']);
    } finally { lock.releaseLock(); }
    return json({ ok: true });
  } catch (err) {
    return json({ ok: false, error: String(err) });
  }
}

function json(o) {
  return ContentService.createTextOutput(JSON.stringify(o)).setMimeType(ContentService.MimeType.JSON);
}

Spreadsheet schema (sheet “RSVPs”)

ColHeaderValues
ATimestampDate/time the RSVP was received
BGuest NameText, 2–100 chars
CGuest Count1–20 if attending, 0 if declined
DAttendanceattending | declined
EEventsmehendi, reception (comma-separated)
FMessageOptional, up to 500 chars
GSourcewebsite | TEST

Event summary

  • Mehendi — Tuesday, 5 January 2027, 4:00 PM onwards
    Evane Muffadal Hall, P7, Continental Heights, Rambhau Bhogle Marg, Mazgaon, Mumbai 400010
  • Reception — Thursday, 7 January 2027, 7:00 PM onwards
    Hatemi Masjid, 4th Floor, Mathar Pakhadi Rd, Thakkar Estate, Mazgaon, Mumbai 400010