{ "kind": "open-flow-workflow", "version": 1, "name": "Newsletter Subscriber Enrollment", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_30b4576c9005fdc4": { "name": "Newsletter signup request", "kind": "webhook", "bodyFields": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "topic" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "email" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "frequency" } ], "method": "POST", "options": { "responseBody": { "kind": "workflow-outputs", "output": "body", "timeoutMs": 60000 }, "responseHeaders": { "Cache-Control": "no-store" } } }, "n_5fd2bfcc0d2ed2d2": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_30b4576c9005fdc4", "output": "body" } ] }, "baseId": { "kind": "value", "value": "YOUR_AIRTABLE_BASE_ID" }, "tableIdOrName": { "kind": "value", "value": "YOUR_SUBSCRIBERS_TABLE_ID" } }, "name": "Prepare newsletter subscription", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "baseId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "tableIdOrName" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "Base ID in the format appXXXXXXXXXXXXXX." }, "nullable": false, "handle": "baseId" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "Table ID or table name accepted by the Airtable path parameter." }, "nullable": false, "handle": "tableIdOrName" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "data" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "payload" } ], "name": "Prepare newsletter subscription", "moduleId": "n_8064bbd9db923e5e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_6fa84c7bbcf5de7a": { "inputs": { "endpoint": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "endpoint" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "payload" } ] } }, "name": "Upsert subscription by email", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "endpoint" }, { "jsonSchema": {}, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "name": "Upsert subscription by email", "executor": { "providerId": "airtable", "method": "PATCH", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "json", "kind": "connector-proxy" } } }, "n_c8fb5191b5c707d7": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6fa84c7bbcf5de7a", "output": "body" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6fa84c7bbcf5de7a", "output": "statusCode" } ] }, "bodyEncoding": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6fa84c7bbcf5de7a", "output": "bodyEncoding" } ] } }, "name": "Check subscription upsert", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "const": "base64" }, "nullable": true, "handle": "bodyEncoding" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "needsFallback" } ], "name": "Check subscription upsert", "moduleId": "n_220f63bd4faf4fbf", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_843d2a64b4157a83": { "inputs": {}, "name": "Duplicate email records?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "duplicate_email", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_c8fb5191b5c707d7", "output": "needsFallback" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_3dc8556f08b7d0a4": { "inputs": { "baseId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "baseId" } ] }, "tableIdOrName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "tableIdOrName" } ] }, "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "data" } ] } }, "name": "Read every duplicate email match", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "Base ID in the format appXXXXXXXXXXXXXX." }, "nullable": false, "handle": "baseId" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "Table ID or table name accepted by the Airtable path parameter." }, "nullable": false, "handle": "tableIdOrName" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "data" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1, "description": "Record ID in the format recXXXXXXXXXXXXXX." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Field values to write into the existing Airtable record." } }, "required": [ "id", "fields" ], "additionalProperties": false, "description": "Record payload used when updating Airtable records." }, "minItems": 1, "maxItems": 10, "description": "Records to update in the Airtable table." }, "nullable": false, "handle": "records" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "expectedId" } ], "name": "Read every duplicate email match", "moduleId": "n_860932add4e1218e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "airtable.list_records" } ] } ] } }, "n_0b001151f215c4c7": { "inputs": { "baseId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "baseId" } ] }, "tableIdOrName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "tableIdOrName" } ] }, "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3dc8556f08b7d0a4", "output": "records" } ] }, "typecast": { "kind": "value", "value": false }, "returnFieldsByFieldId": { "kind": "value", "value": false } }, "name": "Update first duplicate subscription", "kind": "task", "task": { "inputs": [ { "description": "Base ID in the format appXXXXXXXXXXXXXX.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Base ID in the format appXXXXXXXXXXXXXX." }, "nullable": false, "handle": "baseId" }, { "description": "Table ID or table name accepted by the Airtable path parameter.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Table ID or table name accepted by the Airtable path parameter." }, "nullable": false, "handle": "tableIdOrName" }, { "description": "Records to update in the Airtable table.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "minLength": 1, "description": "Record ID in the format recXXXXXXXXXXXXXX." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Field values to write into the existing Airtable record." } }, "required": [ "id", "fields" ], "additionalProperties": false, "description": "Record payload used when updating Airtable records." }, "minItems": 1, "maxItems": 10, "description": "Records to update in the Airtable table." }, "nullable": false, "handle": "records" }, { "description": "Whether Airtable should coerce incoming values to compatible field types.", "jsonSchema": { "type": "boolean", "description": "Whether Airtable should coerce incoming values to compatible field types." }, "nullable": true, "handle": "typecast", "value": null }, { "description": "Whether Airtable should return field IDs instead of field names in record objects.", "jsonSchema": { "type": "boolean", "description": "Whether Airtable should return field IDs instead of field names in record objects." }, "nullable": true, "handle": "returnFieldsByFieldId", "value": null } ], "outputs": [ { "description": "Records returned by Airtable after update.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable after update." }, "nullable": false, "handle": "records" } ], "name": "Update first duplicate subscription", "executor": { "kind": "connector", "action": "airtable.update_records" } } }, "n_74bd0b5229f7419c": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6fa84c7bbcf5de7a", "output": "body" } ] }, "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "data" } ] } }, "name": "Verify upserted subscriber", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "data" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "recipient" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "message" } ], "name": "Verify upserted subscriber", "moduleId": "n_15616977ce60b741", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9cbf9b593445ad96": { "inputs": { "records": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0b001151f215c4c7", "output": "records" } ] }, "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5fd2bfcc0d2ed2d2", "output": "data" } ] }, "expectedId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3dc8556f08b7d0a4", "output": "expectedId" } ] } }, "name": "Verify first duplicate subscriber", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Record ID." }, "createdTime": { "type": "string", "description": "Record creation timestamp returned by Airtable." }, "fields": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Record fields keyed by field name or field ID." }, "commentCount": { "type": "integer", "description": "Comment count returned by Airtable when enabled on the endpoint." } }, "required": [ "id", "fields" ], "additionalProperties": true, "description": "Airtable record." }, "description": "Records returned by Airtable after update." }, "nullable": false, "handle": "records" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "data" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "expectedId" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "recipient" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "message" } ], "name": "Verify first duplicate subscriber", "moduleId": "n_bd72de0239abbc14", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_5e8b538b17dbe66c": { "inputs": { "to": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bd0b5229f7419c", "output": "recipient" }, { "kind": "node", "nodeId": "n_9cbf9b593445ad96", "output": "recipient" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bd0b5229f7419c", "output": "subject" }, { "kind": "node", "nodeId": "n_9cbf9b593445ad96", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bd0b5229f7419c", "output": "message" }, { "kind": "node", "nodeId": "n_9cbf9b593445ad96", "output": "message" } ] }, "isHtml": { "kind": "value", "value": false } }, "name": "Send subscription confirmation", "kind": "task", "task": { "inputs": [ { "description": "Primary recipient email address.", "jsonSchema": { "type": "string", "description": "Primary recipient email address." }, "nullable": true, "handle": "to", "value": null }, { "description": "Additional To recipients.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Additional To recipients." }, "nullable": true, "handle": "extraRecipients", "value": null }, { "description": "Cc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Cc recipients." }, "nullable": true, "handle": "cc", "value": null }, { "description": "Bcc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Bcc recipients." }, "nullable": true, "handle": "bcc", "value": null }, { "description": "Email subject line.", "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject", "value": null }, { "description": "Email body content.", "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body", "value": null }, { "description": "Set to true when the body content is already HTML.", "jsonSchema": { "type": "boolean", "description": "Set to true when the body content is already HTML." }, "nullable": true, "handle": "isHtml", "value": null }, { "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "filename": { "type": "string", "description": "Attachment filename; omit for an unnamed attachment." }, "mimeType": { "type": "string", "description": "MIME type without parameters; defaults to application/octet-stream." }, "contentBase64": { "type": "string", "description": "Standard padded Base64 content. Empty string represents a zero-byte file. Supply this or fileUrl." }, "fileUrl": { "type": "string", "format": "uri", "description": "Public URL to download the attachment. Supply this or contentBase64." }, "contentId": { "type": "string", "minLength": 1, "description": "Bare Content-ID without cid: or angle brackets; reference it as cid: in HTML." }, "disposition": { "type": "string", "enum": [ "inline", "attachment" ], "description": "Defaults to inline when contentId is set, otherwise attachment. Inline requires contentId." } }, "additionalProperties": false, "description": "An attachment or CID inline resource.", "oneOf": [ { "required": [ "contentBase64" ] }, { "required": [ "fileUrl" ] } ] }, "maxItems": 100, "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Verified Gmail send-as alias to use in the From header.", "jsonSchema": { "type": "string", "description": "Verified Gmail send-as alias to use in the From header." }, "nullable": true, "handle": "fromEmail", "value": null } ], "outputs": [ { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "threadId" } ], "name": "Send subscription confirmation", "executor": { "kind": "connector", "action": "gmail.send_email" } } }, "n_854e35e73a211077": { "inputs": { "messageId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5e8b538b17dbe66c", "output": "messageId" } ] } }, "name": "Confirm email acceptance", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" } ], "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" } ], "name": "Confirm email acceptance", "moduleId": "n_a62f81597c60be98", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_33b6ba84ce047432": { "name": "Subscription confirmation", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" } ], "outputMappings": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_854e35e73a211077", "output": "body" } ] } } } }, "edges": [ { "source": "n_30b4576c9005fdc4", "target": "n_5fd2bfcc0d2ed2d2" }, { "source": "n_5fd2bfcc0d2ed2d2", "target": "n_6fa84c7bbcf5de7a" }, { "source": "n_6fa84c7bbcf5de7a", "target": "n_c8fb5191b5c707d7" }, { "source": "n_c8fb5191b5c707d7", "target": "n_843d2a64b4157a83" }, { "source": "n_843d2a64b4157a83", "target": "n_74bd0b5229f7419c", "sourceHandle": "otherwise" }, { "source": "n_843d2a64b4157a83", "target": "n_3dc8556f08b7d0a4", "sourceHandle": "duplicate_email" }, { "source": "n_3dc8556f08b7d0a4", "target": "n_0b001151f215c4c7" }, { "source": "n_0b001151f215c4c7", "target": "n_9cbf9b593445ad96" }, { "source": "n_74bd0b5229f7419c", "target": "n_5e8b538b17dbe66c" }, { "source": "n_9cbf9b593445ad96", "target": "n_5e8b538b17dbe66c" }, { "source": "n_5e8b538b17dbe66c", "target": "n_854e35e73a211077" }, { "source": "n_854e35e73a211077", "target": "n_33b6ba84ce047432" } ] } }, "modules": { "n_8064bbd9db923e5e": { "name": "Prepare newsletter subscription", "imports": [], "source": "export default function prepareNewsletterEnrollment({ body, baseId, tableIdOrName }) {\n if (!body || typeof body !== 'object' || Array.isArray(body)) throw new Error('Provide a signup object.');\n const text = (value, label) => {\n if (typeof value !== 'string' || !value.trim()) throw new Error('Provide ' + label + '.');\n return value.trim();\n };\n const email = text(body.email, 'one email address'), topic = text(body.topic, 'a topic');\n // The single-address grammar used by HTML email fields; no display names or recipient lists.\n if (!/^[a-zA-Z0-9.!#$%&'*+\\-/=?^_`{|}~]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/.test(email)) throw new Error('Provide one email address.');\n if (email.includes(\"'\")) throw new Error('This template does not support apostrophes in email addresses.');\n if (!['daily', 'weekly', 'surprise me'].includes(body.frequency)) throw new Error('Choose daily, weekly or surprise me.');\n baseId = text(baseId, 'your Airtable base ID'); tableIdOrName = text(tableIdOrName, 'your subscribers table ID');\n if (!/^app[a-zA-Z0-9]+$/.test(baseId) || !/^tbl[a-zA-Z0-9]+$/.test(tableIdOrName)) throw new Error('Configure your own Airtable base and table IDs.');\n const fields = { Email: email, Topic: topic, Status: 'active', Interval: body.frequency === 'surprise me' ? 'surprise' : body.frequency, 'Start Day': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][new Date().getUTCDay()] };\n return { baseId, tableIdOrName, data: { fields, recipient: email, subject: 'Learn something every day confirmation', message: 'This is to confirm your request to subscribe to \"Learn something every day!\" - a free service to send you facts about your favourite topics.\\n\\nTopic: ' + topic + '\\nSchedule: ' + body.frequency }, endpoint: '/v0/' + baseId + '/' + tableIdOrName, payload: { typecast: false, performUpsert: { fieldsToMergeOn: ['Email'] }, records: [{ fields }] } };\n}\n" }, "n_220f63bd4faf4fbf": { "name": "Check subscription upsert", "imports": [], "source": "export default function assessNewsletterUpsert({ body, statusCode, bodyEncoding }) {\n if (bodyEncoding !== null) throw new Error('Airtable returned an unexpected encoded response.');\n if (statusCode === 200) return { needsFallback: false };\n if (statusCode === 422 && body && typeof body === 'object' && !Array.isArray(body) && typeof body.error?.message === 'string' && body.error.message.includes('Cannot update more than one record')) return { needsFallback: true };\n throw new Error('Airtable did not accept the subscription upsert (HTTP ' + statusCode + ').');\n}\n" }, "n_860932add4e1218e": { "name": "Read every duplicate email match", "imports": [], "source": "export default async function readNewsletterDuplicates({ baseId, tableIdOrName, data }, context) {\n const records = [], offsets = new Set();\n let offset;\n do {\n context.signal?.throwIfAborted();\n const page = await context.actions.call('airtable.list_records', { baseId, tableIdOrName, fields: ['Email'], filterByFormula: \"AND({Email} = '\" + data.fields.Email + \"')\", pageSize: 100, ...(offset ? { offset } : {}) });\n if (!Array.isArray(page.records)) throw new Error('Airtable did not return matching records.');\n records.push(...page.records);\n const next = page.offset;\n if (next === null || next === undefined) break;\n if (typeof next !== 'string' || !next || offsets.has(next)) throw new Error('Airtable returned an invalid or repeated pagination offset.');\n offsets.add(next); offset = next;\n } while (true);\n const first = records[0];\n if (!first || typeof first.id !== 'string' || !/^rec[a-zA-Z0-9]+$/.test(first.id) || typeof first.fields?.Email !== 'string' || first.fields.Email.toLowerCase() !== data.fields.Email.toLowerCase()) throw new Error('Airtable did not identify the first matching subscriber.');\n return { records: [{ id: first.id, fields: data.fields }], expectedId: first.id };\n}\n" }, "n_15616977ce60b741": { "name": "Verify upserted subscriber", "imports": [], "source": "function verifyNewsletterSubscriber({ records, data, expectedId }) {\n if (!Array.isArray(records) || records.length !== 1) throw new Error('Airtable did not confirm one saved subscriber.');\n const record = records[0];\n if (!record || typeof record.id !== 'string' || !/^rec[a-zA-Z0-9]+$/.test(record.id) || (expectedId && record.id !== expectedId) || !record.fields || Object.entries(data.fields).some(([key, value]) => record.fields[key] !== value)) throw new Error('Airtable did not confirm the saved subscription fields.');\n return { recipient: data.recipient, subject: data.subject, message: data.message };\n}\n\nexport default function({body,data}) { return verifyNewsletterSubscriber({records:body?.records,data,expectedId:\"\"}); }\n" }, "n_bd72de0239abbc14": { "name": "Verify first duplicate subscriber", "imports": [], "source": "export default function verifyNewsletterSubscriber({ records, data, expectedId }) {\n if (!Array.isArray(records) || records.length !== 1) throw new Error('Airtable did not confirm one saved subscriber.');\n const record = records[0];\n if (!record || typeof record.id !== 'string' || !/^rec[a-zA-Z0-9]+$/.test(record.id) || (expectedId && record.id !== expectedId) || !record.fields || Object.entries(data.fields).some(([key, value]) => record.fields[key] !== value)) throw new Error('Airtable did not confirm the saved subscription fields.');\n return { recipient: data.recipient, subject: data.subject, message: data.message };\n}\n" }, "n_a62f81597c60be98": { "name": "Confirm email acceptance", "imports": [], "source": "export default function confirmNewsletterEnrollment({ messageId }) {\n if (typeof messageId !== 'string' || !messageId.trim()) throw new Error('Gmail did not confirm acceptance of the confirmation email.');\n return { body: { subscribed: true, confirmationAccepted: true } };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }