Track daily PG&E energy costs with Airtop and email notifications
Pull contacts, verify each address with BillionVerify, and continue to Airtop — only deliverable addresses get through.
Why verify before the send
Sending to invalid, risky, catch-all, or disposable addresses spikes your bounce rate and erodes sender reputation. A verification gate before the Airtop step removes that risk automatically — only deliverable addresses continue, the rest are flagged.
The workflow
BillionVerify — verification sits right before the send.
Node by node
- 1Schedule TriggerTrigger· n8n
Starts the workflow — on a schedule, a webhook, or manually while you test.
- 2VariablesSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 3Create sessionSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 4Create browser windowSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 5Type usernameSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 6Type passwordSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 7Close modal (if any)Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 8Wait 5 secsSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 9Go to "Energy Usage Details"Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 10Go to "Energy Costs"Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 11Go to "Electricity and Gas"Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 12Extract CostsSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 13End sessionSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 14Verify Email (BillionVerify)Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 15IF deliverableLogic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 16Send emailSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
Workflow JSON
Copy or download this workflow, then import it in n8n (Workflows → Import from File / Paste). Install the BillionVerify community node first, then add your API key credential.
{
"name": "Track daily PG&E energy costs with Airtop and email notifications + BillionVerify",
"nodes": [
{
"id": "814cc9ac-382b-42b3-b5b8-90eda0dc2889",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-2640,
100
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.2
},
{
"id": "310bfb08-2086-4f0f-8790-02c2c186bae2",
"name": "Type password",
"type": "n8n-nodes-base.airtop",
"position": [
-1540,
100
],
"parameters": {
"text": "={{ $('Variables').item.json.PGE_Password }}",
"resource": "interaction",
"windowId": "={{ $('Create browser window').item.json.windowId }}",
"operation": "type",
"sessionId": "={{ $('Create session').item.json.sessionId }}",
"pressEnterKey": true,
"additionalFields": {
"waitForNavigation": "networkidle0"
},
"elementDescription": "PASSWORD Text Box"
},
"typeVersion": 1
},
{
"id": "b0de782e-9c54-4a40-b7e8-8e7ab3655986",
"name": "Variables",
"type": "n8n-nodes-base.set",
"position": [
-2420,
100
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "3a765a8e-14d8-4a32-b894-b6f90e5db246",
"name": "PGE_Username",
"type": "string",
"value": ""
},
{
"id": "81a94ea1-714b-4f9f-b63a-47fee5f51e3a",
"name": "PGE_Password",
"type": "string",
"value": ""
},
{
"id": "e90a5678-4c62-443c-ab1d-57efd6ca69eb",
"name": "Email",
"type": "string",
"value": ""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "69cca1c7-9a83-43a7-95e7-98ba24f9575b",
"name": "Go to \"Energy Costs\"",
"type": "n8n-nodes-base.airtop",
"position": [
-660,
100
],
"parameters": {
"resource": "interaction",
"windowId": "={{ $('Create browser window').item.json.data.windowId }}",
"sessionId": "={{ $('Create session').item.json.sessionId }}",
"additionalFields": {
"waitForNavigation": "load"
},
"elementDescription": "ENERGY COSTS"
},
"typeVersion": 1
},
{
"id": "184206ce-4b4d-4bb0-b468-cbb61f45b61b",
"name": "Go to \"Electricity and Gas\"",
"type": "n8n-nodes-base.airtop",
"onError": "continueRegularOutput",
"position": [
-440,
100
],
"parameters": {
"resource": "interaction",
"windowId": "={{ $('Create browser window').item.json.data.windowId }}",
"sessionId": "={{ $('Create session').item.json.sessionId }}",
"additionalFields": {
"waitForNavigation": "networkidle0"
},
"elementDescription": "COMBINED"
},
"typeVersion": 1
},
{
"id": "424b5209-97e7-4eef-a496-ac9f08d84d3d",
"name": "Extract Costs",
"type": "n8n-nodes-base.airtop",
"notes": "Some PG&E accounts have a \"Combined\" view for gas and electricity",
"position": [
-220,
100
],
"parameters": {
"prompt": "Extract the daily energy costs from the webpage content, including both natural gas and electricity costs. Format the information as a daily update email, listing the costs from the most recent date to the earliest. Ensure the email format is clear and concise, without including a subject line or greeting. Include the date, total combined costs, natural gas costs, and electricity costs for each day. \n\nIf natural gas costs are not provided, ignore them, ignore Total Combined Costs and report only on electricity \n\nFor example, if the webpage content provides the following data:\n\n- Date: 2023-10-01, Total Combined Costs: $15.00, Natural Gas Costs: $5.00, Electricity Costs: $10.00\n- Date: 2023-09-30, Total Combined Costs: $14.50, Natural Gas Costs: $4.50, Electricity Costs: $10.00\n\nThe output should be formatted as an easy to read email:\n\n<!DOCTYPE html>\n<html>\n<body>\n<p>October 1, 2023</p>\n<p>Total Combined Costs: $15.00<br>\nNatural Gas Costs: $5.00<br>\nElectricity Costs: $10.00</p>\n\n<p>September 30, 2023</p>\n<p>Total Combined Costs: $14.50<br>\nNatural Gas Costs: $4.50<br>\nElectricity Costs: $10.00</p>\n</body>\n</html>\n",
"resource": "extraction",
"windowId": "={{ $('Create browser window').item.json.data.windowId }}",
"operation": "query",
"sessionId": "={{ $('Create session').item.json.sessionId }}",
"additionalFields": {}
},
"notesInFlow": true,
"typeVersion": 1
},
{
"id": "b0cf1ebc-157d-4ab3-9ed6-267c81293feb",
"name": "Go to \"Energy Usage Details\"",
"type": "n8n-nodes-base.airtop",
"position": [
-880,
100
],
"parameters": {
"resource": "interaction",
"windowId": "={{ $('Create browser window').item.json.windowId }}",
"sessionId": "={{ $('Create session').item.json.sessionId }}",
"additionalFields": {
"waitForNavigation": "load"
},
"elementDescription": "Click on the box that says ENERGY USAGE DETAILS See usage & costs over time"
},
"typeVersion": 1
},
{
"id": "485f8071-9c54-4f79-9378-d354260b2038",
"name": "Wait 5 secs",
"type": "n8n-nodes-base.wait",
"position": [
-1100,
100
],
"webhookId": "371deac7-bd64-4385-8bc5-a14a0db2bcc7",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "87d68490-1064-4180-89d9-534c9308c6c9",
"name": "Close modal (if any)",
"type": "n8n-nodes-base.airtop",
"position": [
-1320,
100
],
"parameters": {
"resource": "interaction",
"windowId": "={{ $('Create browser window').item.json.windowId }}",
"sessionId": "={{ $('Create session').item.json.sessionId }}",
"additionalFields": {},
"elementDescription": "If there is a modal on the page, click on the button to dismiss the modal"
},
"typeVersion": 1
},
{
"id": "80018e55-6557-4641-a07b-926517a72bb0",
"name": "Create session",
"type": "n8n-nodes-base.airtop",
"position": [
-2200,
100
],
"parameters": {
"profileName": "cesar-prod",
"timeoutMinutes": 5
},
"typeVersion": 1
},
{
"id": "05711cf0-9c20-4f41-854e-dea872eee3d8",
"name": "Create browser window",
"type": "n8n-nodes-base.airtop",
"position": [
-1980,
100
],
"parameters": {
"url": "https://m.pge.com/",
"resource": "window",
"getLiveView": true,
"disableResize": true,
"additionalFields": {
"waitUntil": "load"
}
},
"typeVersion": 1
},
{
"id": "d673347d-ef40-4349-a7e7-2ba594400d2c",
"name": "Type username",
"type": "n8n-nodes-base.airtop",
"position": [
-1760,
100
],
"parameters": {
"text": "={{ $('Variables').item.json.PGE_Username }}",
"resource": "interaction",
"operation": "type",
"additionalFields": {},
"elementDescription": "USERNAME text box"
},
"typeVersion": 1
},
{
"id": "b64a04b1-d00b-4d04-b9e3-7d2c86800923",
"name": "Send email",
"type": "n8n-nodes-base.gmail",
"position": [
0,
200
],
"webhookId": "7586d2f6-00b8-41ee-89d0-f2768b402165",
"parameters": {
"sendTo": "={{ $('Variables').item.json.Email }}",
"message": "={{ $json['data'].modelResponse }}",
"options": {
"senderName": "Airtop Monitor",
"appendAttribution": false
},
"subject": "Daily energy costs report"
},
"typeVersion": 2.1
},
{
"id": "d402443c-ed67-4df7-b5c8-032f4a2ea941",
"name": "End session",
"type": "n8n-nodes-base.airtop",
"position": [
0,
0
],
"parameters": {
"operation": "terminate",
"sessionId": "={{ $('Create session').item.json.sessionId }}"
},
"typeVersion": 1
},
{
"id": "076e7eed-e71f-4ef7-8038-ea3dcc188b9c",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2480,
-40
],
"parameters": {
"color": 5,
"height": 340,
"content": "## Heads up!\nTo get this workflow running correctly, please enter your PG&E credentials below"
},
"typeVersion": 1
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Variables').item.json.Email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
-360,
200
],
"name": "Verify Email (BillionVerify)",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-180,
200
],
"name": "IF deliverable"
}
],
"connections": {
"Variables": {
"main": [
[
{
"node": "Create session",
"type": "main",
"index": 0
}
]
]
},
"Wait 5 secs": {
"main": [
[
{
"node": "Go to \"Energy Usage Details\"",
"type": "main",
"index": 0
}
]
]
},
"Extract Costs": {
"main": [
[
{
"node": "End session",
"type": "main",
"index": 0
},
{
"node": "Verify Email (BillionVerify)",
"type": "main",
"index": 0
}
]
]
},
"Type password": {
"main": [
[
{
"node": "Close modal (if any)",
"type": "main",
"index": 0
}
]
]
},
"Type username": {
"main": [
[
{
"node": "Type password",
"type": "main",
"index": 0
}
]
]
},
"Create session": {
"main": [
[
{
"node": "Create browser window",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Variables",
"type": "main",
"index": 0
}
]
]
},
"Close modal (if any)": {
"main": [
[
{
"node": "Wait 5 secs",
"type": "main",
"index": 0
}
]
]
},
"Go to \"Energy Costs\"": {
"main": [
[
{
"node": "Go to \"Electricity and Gas\"",
"type": "main",
"index": 0
}
]
]
},
"Create browser window": {
"main": [
[
{
"node": "Type username",
"type": "main",
"index": 0
}
]
]
},
"Go to \"Electricity and Gas\"": {
"main": [
[
{
"node": "Extract Costs",
"type": "main",
"index": 0
}
]
]
},
"Go to \"Energy Usage Details\"": {
"main": [
[
{
"node": "Go to \"Energy Costs\"",
"type": "main",
"index": 0
}
]
]
},
"Verify Email (BillionVerify)": {
"main": [
[
{
"node": "IF deliverable",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable": {
"main": [
[
{
"node": "Send email",
"type": "main",
"index": 0
}
],
[]
]
}
},
"settings": {
"executionOrder": "v1"
}
}When to use this
- Cleaning a list before a Airtop send or sync.
- Protecting Airtop deliverability and sender reputation.
- Keeping bounce rates low so your sending is never throttled.
FAQ
Why verify before sending in Airtop?
Verifying first keeps your bounce rate low, which protects your sender reputation and your results.
How do I import this workflow?
Download the JSON, then in n8n go to Workflows → Import from File (or paste it). Install the BillionVerify community node and add your API key credential.
What happens to risky or catch-all addresses?
They are routed to the false branch and excluded from the send. You decide whether to retry, review, or drop them.
Add verification to your workflow
Create a free account, grab your API key, and stop bounces before they happen.
Get started free