Webhook Events Reference
Complete reference for all webhook event types and their payload structures. Each event sends a JSON payload to your webhook URL via HTTP POST.
Event Types Overview
| Event Type | Trigger | Typical Use Cases |
|---|---|---|
customer_created |
New customer is created | CRM sync, welcome emails |
customer_status_change |
Customer status is updated | Sales pipeline, notifications |
project_email_sent |
Email sent for a project | Communication tracking |
project_sms_sent |
SMS sent for a project | Communication tracking |
published_proposal_link |
Proposal link is published | Team notifications, analytics |
contract_signed |
Contract is signed by customer | Sales tracking, notifications |
recent_draft_project |
Draft project created or updated | Work-in-progress tracking |
customer_created
Triggered when a new customer is created in Solar Proof.
customer_created sends a single object (not an array). Access fields directly without using [0].
Event Payload
{
"customer_id": 141472,
"customer_uid": "a1b2c3d4",
"first_name": "Hussain",
"surname": "Shahab",
"customer_name": "Hussain Shahab",
"phone_primary": "0412345678",
"phone_secondary": "",
"customer_email": "s.hussainshahab@gmail.com",
"company_name": "",
"company_phone": "",
"abn": "",
"gst_registered": 0,
"customer_type_id": 1,
"customer_status_id": 1,
"site_address": "45 Thuringowa Dr, Kirwan QLD 4817, Australia",
"customer_created_at": "2025-10-16T12:00:00Z",
"customer_notes": "",
"assigned_user_id": 16,
"assigned_user_name": "Chris Taeni",
"assigned_user_email": "phoenix.cst@gmail.com",
"company_name_full": "Solar Proof"
}
Payload Fields
| Field | Type | Description |
|---|---|---|
customer_id |
integer | Unique customer ID |
customer_uid |
string | Unique customer identifier (UID) |
first_name |
string | Customer's first name |
surname |
string | Customer's surname/last name |
customer_name |
string | Full customer name |
customer_email |
string | Customer email address |
phone_primary |
string | Primary phone number |
phone_secondary |
string | Secondary phone number |
company_name |
string | Customer's company name (if commercial) |
gst_registered |
integer | GST registration status (0 or 1) |
customer_type_id |
integer | Customer type: 1 = Residential, 2 = Commercial |
customer_status_id |
integer | Initial customer status ID |
site_address |
string | Customer's site address |
customer_created_at |
string | ISO 8601 timestamp when customer was created |
assigned_user_id |
integer | ID of the Solar Proof user assigned to this customer |
assigned_user_name |
string | Name of assigned user |
company_name_full |
string | Solar installation company name |
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const customer = req.body; // Single object, NOT an array!
// Sync to CRM
await crm.createContact({
id: customer.customer_id,
email: customer.customer_email,
firstName: customer.first_name,
lastName: customer.surname,
phone: customer.phone_primary,
address: customer.site_address,
assignedTo: customer.assigned_user_name,
source: 'Solar Proof'
});
res.status(200).send('OK');
});
customer_status_change
Triggered when a customer's status is changed (e.g., from Lead to Signed).
Event Payload
[
{
"customer_id": 141472,
"first_name": "Hussain",
"surname": "Shahab",
"customer_name": "Hussain Shahab",
"phone_primary": "",
"phone_secondary": "",
"customer_email": "s.hussainshahab@gmail.com",
"company_name": "",
"company_phone": "",
"abn": "",
"gst_registered": 0,
"customer_type_id": 1,
"customer_status_id": 1,
"site_address": "45 Thuringowa Dr, Kirwan QLD 4817, Australia",
"customer_created_at": "2025-10-16T00:00:00Z",
"customer_notes": "",
"assigned_user_id": 16,
"assigned_user_name": "Chris Taeni",
"assigned_user_email": "phoenix.cst@gmail.com",
"assigned_user_phone": "0411 549 054",
"company_name_full": "Solar Proof",
"customer_status_name": "Lead"
}
]
Payload Fields
| Field | Type | Description |
|---|---|---|
customer_id |
integer | Unique customer ID in Solar Proof |
first_name |
string | Customer's first name |
surname |
string | Customer's surname/last name |
customer_name |
string | Full customer name |
customer_email |
string | Customer email address |
phone_primary |
string | Primary phone number |
customer_status_id |
integer | New status ID |
customer_status_name |
string | New status name (e.g., "Lead", "Signed") |
site_address |
string | Customer's site address |
assigned_user_id |
integer | ID of the Solar Proof user assigned to this customer |
assigned_user_name |
string | Name of assigned user |
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body; // Array of events
const event = events[0]; // Get first event
// Update CRM status
await crm.updateContact(event.customer_id, {
status: event.customer_status_name,
assignedTo: event.assigned_user_name
});
res.status(200).send('OK');
});
project_email_sent
Triggered when an email is sent for a project (e.g., proposal email, follow-up). Returns comprehensive project, customer, equipment, and company information.
Event Payload
[
{
"activity_id": 266268,
"activity_type": "email_sent",
"email_sent_at": "2025-10-16T12:37:44Z",
"project_id": 118825,
"project_name": "45 Thuringowa Dr - Solar project (#118825)",
"project_address": "{\"streetnum\":\"45\",\"street\":\"Thuringowa Dr\",\"suburb\":\"Kirwan\",\"state\":\"QLD\",\"postcode\":\"4817\",\"country\":\"AU\"}",
"proposal_url": null,
"viewing_link": "uploads/fa00dfba91388ede4483011b455d93/pdfs/118825/proposal_118825.pdf",
"signed_at": null,
"installation_date": null,
"system_size_kw": 0,
"panel_quantity": 21,
"panel_wattage": "400",
"quote_amount": "20830.00",
"inverter_quantity": 1,
"battery_quantity": 1,
"deposit_amount": 4166,
"deposit_percentage": 20,
"has_signed_pdf": 0,
"panel_brand": "TCL SunPower Global",
"panel_model": "SPR-MAX3-400",
"panel_wattage_spec": "400",
"inverter_brand": "SMA Australia Pty Ltd",
"inverter_model": "STP10000TL-20",
"inverter_capacity": "10",
"inverter_series": "Sunny Tripower",
"battery_brand": "Alpha-ESS",
"battery_model": "SMILE-G3-BAT-10.1P",
"battery_name": "Alpha-ESS SMILE-G3-BAT-10.1P",
"battery_capacity": 10.1,
"customer_id": 141472,
"customer_name": "Hussain Shahab",
"customer_email": "s.hussainshahab@gmail.com",
"customer_phone": "",
"customer_company_name": "",
"user_id": 16,
"company_name": "Solar Proof",
"user_name": "Chris Taeni",
"user_email": "phoenix.cst@gmail.com",
"company_phone": "08 7160 0127",
"signed_pdf_url": null,
"payment_terms": {
"type": "cash",
"description": null,
"term_months": 0,
"repayment_amount": 0,
"repayment_frequency": "Weekly"
}
}
]
Payload Fields
| Field | Type | Description |
|---|---|---|
| Activity Information | ||
activity_id |
integer | Unique activity log ID |
activity_type |
string | Always "email_sent" for this event |
email_sent_at |
string | ISO 8601 timestamp when email was sent |
| Project Information | ||
project_id |
integer | Unique project/quotation ID |
project_name |
string | Name/nickname of the project |
project_address |
string | JSON-encoded installation site address |
system_size_kw |
number | System capacity in kilowatts |
quote_amount |
string | Total quote amount after rebates |
deposit_amount |
number | Deposit amount required |
signed_at |
string | ISO timestamp when contract was signed (null if not signed) |
installation_date |
string | ISO timestamp of planned installation (null if not set) |
| Equipment Details | ||
panel_brand |
string | Solar panel manufacturer |
panel_model |
string | Solar panel model number |
panel_quantity |
integer | Number of solar panels |
panel_wattage |
string | Wattage per panel |
inverter_brand |
string | Inverter manufacturer |
inverter_model |
string | Inverter model number |
inverter_capacity |
string | Inverter capacity in kW |
battery_brand |
string | Battery manufacturer (null if no battery) |
battery_model |
string | Battery model number (null if no battery) |
battery_capacity |
number | Battery capacity in kWh (null if no battery) |
| Customer Information | ||
customer_id |
integer | Unique customer ID |
customer_name |
string | Customer's full name |
customer_email |
string | Customer's email address |
customer_phone |
string | Customer's phone number |
| User/Company Information | ||
user_id |
integer | ID of the Solar Proof user who sent the email |
user_name |
string | Name of the Solar Proof user |
company_name |
string | Solar installation company name |
company_phone |
string | Company phone number |
| Payment Information | ||
payment_terms |
object | Payment terms object containing type, description, term_months, repayment_amount, and repayment_frequency |
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
const event = events[0];
await analytics.trackEvent('project_email_sent', {
customer_id: event.customer_id,
project_id: event.project_id,
user_id: event.user_id,
system_size: event.system_size_kw,
quote_amount: event.quote_amount,
timestamp: new Date(event.email_sent_at)
});
res.status(200).send('OK');
});
project_sms_sent
Triggered when an SMS is sent for a project. Returns comprehensive project and customer information.
Event Payload
[
{
"activity_id": 264533,
"activity_type": "sms_sent",
"sms_sent_at": "2025-10-10T14:23:52Z",
"project_id": 117573,
"project_name": "48 Wright St - Battery project (#117573)",
"project_address": "{\"streetnum\":\"48\",\"street\":\"Wright St\",\"suburb\":\"Paradise\",\"state\":\"SA\",\"postcode\":\"5075\",\"country\":\"AU\"}",
"proposal_url": null,
"viewing_link": "uploads/fa00dfba91388ede4483011b455d93/pdfs/117573/proposal_117573.pdf",
"signed_at": "2025-10-15T00:00:00Z",
"installation_date": null,
"system_size_kw": 0,
"quote_amount": "14974.00",
"panel_quantity": 28,
"inverter_quantity": 1,
"battery_quantity": 1,
"deposit_amount": 4000,
"deposit_percentage": 20,
"has_signed_pdf": 1,
"panel_brand": "Trina Solar Co Ltd",
"panel_model": "TSM-450NEG9R.25",
"panel_wattage_spec": "450",
"inverter_brand": "FOX ESS",
"inverter_model": "KH10/KA10",
"inverter_capacity": "10",
"battery_brand": "Fox ESS",
"battery_model": "ECS4800-H7",
"battery_capacity": 32.61,
"customer_id": 141145,
"customer_name": "John Wilkinson",
"customer_email": "jwilksy@solarproof.com.au",
"customer_phone": "0411 222 222",
"customer_phone_alternate": "",
"customer_company_name": "",
"user_id": 16,
"company_name": "Solar Proof",
"user_name": "Chris Taeni",
"user_email": "phoenix.cst@gmail.com",
"company_sender_phone": "08 7160 0127",
"signed_pdf_url": "https://solarproof.com.au/uploads/fa00dfba91388ede4483011b455d93/pdfs/117573/proposal_117573.pdf",
"payment_terms": {
"type": "cash",
"description": "",
"term_months": 0,
"repayment_amount": 0,
"repayment_frequency": "Weekly",
"balloon_payment": 0
}
}
]
Payload Fields
Similar fields to project_email_sent, with sms_sent_at instead of email_sent_at, and includes company_sender_phone (the phone number used to send the SMS).
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
const event = events[0];
await communicationLog.record({
type: 'sms',
customer_id: event.customer_id,
project_id: event.project_id,
sent_by: event.user_name,
sent_from: event.company_sender_phone,
timestamp: new Date(event.sms_sent_at)
});
res.status(200).send('OK');
});
published_proposal_link
Triggered when a proposal link is published and made accessible to a customer.
Event Payload
[
{
"activity_id": 266666,
"proposal_link": "https://solarproof.com.au/uploads/fa00dfba91388ede4483011b455d93/pdfs/118825/proposal_118825.pdf",
"proposal_short_url": null,
"activity_type": "published_proposal_link",
"link_sent_at": "2025-10-17T12:08:09Z",
"updated_at": "2025-10-17T12:08:09Z",
"project_id": 118825,
"project_name": "45 Thuringowa Dr - Solar project (#118825)",
"project_address": "{\"streetnum\":\"45\",\"street\":\"Thuringowa Dr\",\"suburb\":\"Kirwan\",\"state\":\"QLD\",\"postcode\":\"4817\",\"country\":\"AU\"}",
"signed_at": null,
"installation_date": null,
"system_size_kw": 0,
"panel_quantity": 21,
"quote_amount": "20830.00",
"inverter_quantity": 1,
"battery_quantity": 1,
"deposit_amount": 4166,
"deposit_percentage": 20,
"has_signed_pdf": 0,
"panel_brand": "TCL SunPower Global",
"panel_model": "SPR-MAX3-400",
"panel_wattage_spec": "400",
"inverter_brand": "SMA Australia Pty Ltd",
"inverter_model": "STP10000TL-20",
"inverter_capacity": "10",
"battery_brand": "Alpha-ESS",
"battery_model": "SMILE-G3-BAT-10.1P",
"battery_capacity": 10.1,
"customer_id": 141472,
"customer_name": "Hussain Shahab",
"customer_email": "s.hussainshahab@gmail.com",
"customer_phone": "",
"customer_company_name": "",
"user_id": 16,
"company_name": "Solar Proof",
"user_name": "Chris Taeni",
"user_email": "phoenix.cst@gmail.com",
"company_phone": "08 7160 0127",
"signed_pdf_url": null,
"payment_terms": {
"type": "cash",
"description": "",
"term_months": 0,
"repayment_amount": 0,
"repayment_frequency": "Weekly",
"balloon_payment": 0
}
}
]
Key Payload Fields
| Field | Type | Description |
|---|---|---|
proposal_link |
string | Full URL to the published proposal |
link_sent_at |
string | ISO 8601 timestamp when link was published |
Includes all project, customer, equipment, and company fields similar to project_email_sent.
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
const event = events[0];
await notifications.send({
to: 'sales@company.com',
subject: 'New Proposal Published',
message: `${event.user_name} published a proposal for ${event.customer_name}`,
link: event.proposal_link
});
res.status(200).send('OK');
});
contract_signed
Triggered when a customer signs a contract/proposal. Includes extensive project details and customer information.
Event Payload
[
{
"activity_id": 265815,
"activity_type": "document_signed",
"contract_signed_at": "2025-10-15T03:46:15Z",
"project_id": 118692,
"project_name": "48 Wright St - Upgrade Battery project",
"project_address": "{\"streetnum\":\"48\",\"street\":\"Wright St\",\"suburb\":\"Paradise\",\"state\":\"SA\",\"postcode\":\"5075\",\"country\":\"AU\"}",
"proposal_url": null,
"viewing_link": "uploads/fa00dfba91388ede4483011b455d93/pdfs/118692/proposal_118692.pdf",
"signed_at": "2025-10-15T00:00:00Z",
"installation_date": null,
"system_size_kw": 0,
"panel_quantity": 28,
"panel_wattage": "450",
"quote_amount": "14974.00",
"inverter_quantity": 1,
"battery_quantity": 1,
"deposit_amount": 4000,
"deposit_percentage": 20,
"stc_value": 37,
"stc_quantity": 376,
"has_signed_pdf": 1,
"has_original_pdf": 1,
"panel_brand": "Trina Solar Co Ltd",
"panel_model": "TSM-450NEG9R.25",
"panel_wattage_spec": "450",
"inverter_brand": "FOX ESS",
"inverter_model": "KH10/KA10",
"inverter_capacity": "10",
"inverter_series": "KH/KA",
"battery_brand": "Fox ESS",
"battery_model": "ECS4800-H7",
"battery_name": "Fox ESS ECS4800-H7",
"battery_capacity": "32.61",
"customer_id": 141201,
"customer_name": "Julia Foghorn",
"customer_first_name": "Julia",
"customer_last_name": "Foghorn",
"customer_email": "juliafoghorn@solarproof.com.au",
"customer_phone": "0499 333 444",
"customer_phone_alternate": "",
"customer_company_name": "",
"customer_company_phone": "",
"customer_abn": "",
"user_id": 16,
"company_name": "Solar Proof",
"user_name": "Chris Taeni",
"user_email": "phoenix.cst@gmail.com",
"company_phone": "08 7160 0127",
"company_email": "admin@solarproof.com.au",
"company_address": "764 Cudgen Rd, Kingscliff NSW 2487, Australia",
"signed_pdf_url": null,
"proposal_link": "https://solarproof.com.au/uploads/fa00dfba91388ede4483011b455d93/pdfs/118692/proposal_118692.pdf",
"payment_terms": {
"type": "cash",
"description": "",
"term_months": "0",
"repayment_amount": "0",
"repayment_frequency": "Weekly",
"balloon_payment": "0"
},
"customer_type": "Residential",
"customer_gst_status": "Not GST Registered"
}
]
Additional Fields
| Field | Type | Description |
|---|---|---|
contract_signed_at |
string | ISO timestamp when contract was signed |
stc_value |
number | Small-scale Technology Certificate value |
stc_quantity |
number | Number of STCs |
customer_type |
string | "Residential" or "Commercial" |
customer_gst_status |
string | "GST Registered" or "Not GST Registered" |
company_email |
string | Installation company email |
company_address |
string | Installation company address |
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
const event = events[0];
// Update CRM
await crm.markAsSigned(event.customer_id, {
signedDate: event.contract_signed_at,
projectId: event.project_id,
quoteAmount: event.quote_amount,
depositAmount: event.deposit_amount
});
// Notify installation team
await notifications.sendToTeam({
subject: 'New Contract Signed!',
customer: event.customer_name,
systemSize: event.system_size_kw,
installDate: event.installation_date
});
res.status(200).send('OK');
});
recent_draft_project
Triggered when a draft project is created or updated. Useful for tracking work-in-progress.
Event Payload
[
{
"project_id": 118871,
"project_name": "80 Collins St - Solar project (#118871)",
"project_address": "{\"streetnum\":\"132\",\"street\":\"Grenfell St\",\"suburb\":\"Adelaide\",\"state\":\"SA\",\"postcode\":\"5000\",\"country\":\"AU\"}",
"proposal_url": null,
"created_at": "2025-10-16T03:35:10Z",
"last_edited": "2025-10-16T14:43:53Z",
"system_size_kw": 0,
"panel_quantity": 0,
"panel_wattage": "250",
"quote_amount": "6500.00",
"inverter_quantity": 1,
"battery_quantity": 0,
"deposit_amount": 0,
"deposit_percentage": 0,
"is_draft": 1,
"draft_stage": 8,
"panel_brand": "Jinko Solar Co Ltd",
"panel_model": "JKM250P-60",
"panel_wattage_spec": "250",
"inverter_brand": "Fronius International GmbH",
"inverter_model": "Symo 10.0-3-M",
"inverter_capacity": "10",
"inverter_series": "Symo",
"battery_brand": null,
"battery_model": null,
"battery_name": null,
"battery_capacity": null,
"customer_id": null,
"customer_name": null,
"customer_email": null,
"customer_phone": null,
"customer_company_name": null,
"user_id": 53179,
"company_name": "Solar Proof",
"user_name": "Sam Lawson",
"user_email": "sam@solarproof.com.au",
"payment_terms": {
"type": "cash",
"description": "",
"term_months": 0,
"repayment_amount": 0,
"repayment_frequency": "Weekly",
"balloon_payment": 0
}
}
]
Key Payload Fields
| Field | Type | Description |
|---|---|---|
is_draft |
integer | Always 1 for draft projects |
draft_stage |
integer | Current stage of the draft (1-10) |
created_at |
string | ISO timestamp when draft was created |
last_edited |
string | ISO timestamp of last modification |
customer_id |
null | May be null if customer not yet assigned |
Example Handler
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
const event = events[0];
await dashboard.updateDraft({
projectId: event.project_id,
projectName: event.project_name,
stage: event.draft_stage,
lastEdited: event.last_edited,
assignedTo: event.user_name
});
res.status(200).send('OK');
});
Testing Webhooks
Create Test Events
Trigger events in your Solar Proof account to generate webhook payloads:
- Change a customer's status to trigger
customer_status_change - Send a project email to trigger
project_email_sent - Send an SMS to trigger
project_sms_sent - Publish a proposal to trigger
published_proposal_link - Create or edit a draft project to trigger
recent_draft_project
Webhook Best Practices
Respond Quickly
Your webhook endpoint must respond within 5 seconds. Process complex operations asynchronously:
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
// Respond immediately
res.status(200).send('OK');
// Process asynchronously
await queue.add('process-webhook', events);
});
Handle Array vs Object Format
Important: Most webhook payloads are sent as arrays, but customer_created sends a single object. Handle each accordingly:
app.post('/webhooks/solarproof', async (req, res) => {
const payload = req.body;
// Check if it's customer_created (single object)
if (payload.customer_id && !Array.isArray(payload)) {
// Direct access for customer_created
console.log(payload.customer_id);
}
// All other events (array format)
else if (Array.isArray(payload)) {
const event = payload[0];
console.log(event.customer_id);
}
res.status(200).send('OK');
});
Handle Duplicate Events
Webhooks may be delivered more than once. Use idempotency keys to prevent duplicate processing:
app.post('/webhooks/solarproof', async (req, res) => {
const events = req.body;
const event = events[0];
const idempotencyKey = `${event.activity_type}_${event.activity_id}`;
// Check if already processed
const exists = await db.checkProcessed(idempotencyKey);
if (exists) {
return res.status(200).send('Already processed');
}
// Process and mark as complete
await processEvent(event);
await db.markProcessed(idempotencyKey);
res.status(200).send('OK');
});
Parse JSON Addresses
The project_address field contains JSON-encoded address data. Parse it to access individual components:
const events = req.body;
const event = events[0];
// Parse the address JSON
const address = JSON.parse(event.project_address);
console.log(address.streetnum); // "45"
console.log(address.street); // "Thuringowa Dr"
console.log(address.suburb); // "Kirwan"
console.log(address.state); // "QLD"
console.log(address.postcode); // "4817"
console.log(address.country); // "AU"