Webhooks
Create a Registration
Section titled “Create a Registration”The first step to setting up webhooks calls to your system is to create a registration. The registration is then used to subscribe to various types events. Registrations belong to a specific organization, pulled from the Auth token used when creating the registration. All events are scoped to resources inside this organization UNLESS the includeVisibleOrgEvents registration setting is set to true, in which case you’ll receive events for any organization to which the registered org has been granted access.
You can either create webhook registrations using the API (which will be discussed here), or through the Palmetto Finance UI. To create a registration through the UI, navigate to the “Developer Settings” tab of your organization settings, and click “Create”. This will generate a registration for you. There are a couple of authentication mechanisms available, Server API Key which we provide, Client Headers where you provide a Client Id and Secret, or Basic Authentication Headers, again based upon values provided by you.
import fetch from 'node-fetch';
const url = `{envBaseUrl}/api/webhooks/registrations`;const response = await fetch(url, { method: 'post', body: JSON.stringify({ name: 'a descriptive name', hostUrl: 'https://my.domain.com', includeVisibleOrgEvents: true, }), headers: { 'Content-Type': 'application/json' },});const data = await response.json();console.log(data);The response will contain the webhookRegistrationId of the client, which is used to subscribe to events. As well as the apiKey which is used to verify
webhook calls came from the palmetto finance API. Calls to your service will be made with an api_key header set to the value of the apiKey returned here.
The hostURL parameter should include only the portion of the URL that includes the domain. You will include the remaining path for your URL in the endpointURL parameter in the subscription, as shown below.
Authentication
Section titled “Authentication”In addition to HTTPS verifying that the origin is Palmetto, you can verify that the requests originate from your API registration. There are 3 authentication mechanisms available:
- Server API key
- Client headers
- Client basic authorization header
An overview of each follows. For more detailed implementation information, see the API Reference.
Server API key
Section titled “Server API key”This is the default if you don’t specify an authentication method.
Palmetto generates an API key. It is returned from the registration endpoint, and only once. Store this value, and then on webhook events to your endpoint, an apiKey header will be sent with the API key as the value.
Client headers
Section titled “Client headers”You provide a client ID and a client secret in your registration request. Palmetto stores these and on webhook events to your endpoint, two headers, clientId and clientSecret, will be sent with the respective values.
Client basic authorization header
Section titled “Client basic authorization header”Similar to client headers, you provide a client ID and client secret. Palmetto stores these and on webhook events to your endpoint, a single Authorization header will be sent with a base64 encoding of the two values concatenated together: {clientId}:{clientSecret}.
Subscribe to Events
Section titled “Subscribe to Events”Once you have a registration, you then subscribe to one or more events. There is a catch all, allEvents that we recommend as a starting point for most partners, or you can subscribe to specific events listed further down in this page.
import fetch from 'node-fetch';
const url = `{envBaseUrl}/api/webhooks/registrations/{webhookRegistrationId}/subscriptions`;const response = await fetch(url, { method: 'post', body: JSON.stringify({ endpointUrl: '/myexample/lightreach/receiver', httpMethod: 'POST', eventType: ['allEvents'], }), headers: { 'Content-Type': 'application/json' },});The full URL to your webhook receiver service will be the registration.hostURL + subscription.endpointURL. Per our example above, the complete URL we will send events for this subscription would be https://my.domain.com/myexample/lightreach/receiver.
Retries
Section titled “Retries”When a webhook delivery fails, Palmetto will automatically retry up to 9 times (10 total attempts) using exponential backoff starting at 3 minutes between attempts. Each delivery attempt has a 30-second timeout.
Retryable failures (will be retried):
- 5xx server errors
- 429 Too Many Requests
- Network timeouts or connection errors
Non-retryable failures (immediately marked as failed, no retries):
- 4xx client errors (except 429) — e.g. 401 Unauthorized, 404 Not Found
- Invalid or misconfigured endpoint URL
- Any HTTP status codes added to the subscription’s
nonretryableErrorCodeslist
Auto-disable on Repeated Failures
Section titled “Auto-disable on Repeated Failures”If a subscription accumulates 10 failures, it will be automatically disabled. While disabled, any events that would have been delivered to that subscription are logged as skipped rather than lost. You can re-enable the subscription through the Palmetto Finance UI or API, and skipped events can be replayed at that point.
Event Types
Section titled “Event Types”For a full list of events, see the schema in the API reference.
| event name | Description |
|---|---|
accountCreated | notifies when a new account is created |
accountUpdated | any updates to an account |
activeQuoteExceedsMonthlyPaymentCaps | quote for account exceeds monthly payment cap |
allEvents | subscribe to all events at this path |
allConsumerTaskEvents | subscribe to all consumer events at this path |
allContractEvents | subscribe to all contract events at this path |
allStipulationEvents | subscribe to all stipulation events at this path |
allStipulationsCleared | all stipulations have been cleared |
applicationStatus | any application status changes |
contractApproved | any contract approved |
contractReinstated | a previously voided contract is reinstated |
contractSent | any contract sent |
contractSigned | any contract signed |
contractVoided | any contract voided |
documentStatus | any change to the status of an uploaded document |
documentUploaded | notifies you when a new document is uploaded |
illinoisShineDisclosureSent | Illinois Shines disclosure sent |
illinoisShineDisclosureSigned | Illinois Shines disclosure signed |
milestoneAchieved | any milestone reached |
milestonePackage | events for milestone packages |
milestoneStatusChanged | events for changes to milestone statuses |
quoteCreated | any quote created |
quoteVoided | any quote voided |
requirementStatusChanged | any change to an account requirement status |
requirementCompleted | any account requirement completed |
stipulationAdded. | a single stipulation added |
stipulationCleared | a single stipulation cleared |
termsAndConditionsAccepted | terms and conditions accepted |
Webhook Event Payloads
Section titled “Webhook Event Payloads”Account Events
Section titled “Account Events”Account Created
Section titled “Account Created”This webhook is sent whenever a new account is created
{ "event": "accountCreated", "accountId": "palmetto-finance-account-id", "accountReference" : "account-external-reference", "organizationId": "the id of the organization this account belongs to", "programType": "solar | hvac | newHomes | waterHeater",
}Account Updated
Section titled “Account Updated”This webhook is sent whenever an account is updated
{ "event": "accountUpdated", "accountId": "palmetto-finance-account-id", "accountReference" : "account-external-reference", "updates": { ... any fields updated on the account }, "primaryApplicantEmail": "primary-applicant-email-address", "organizationId": "org-id"}Application Events
Section titled “Application Events”Application Status Event
Section titled “Application Status Event”This webhook is sent whenever there is a change in the status of an credit application.
{ "event" : "applicationStatus", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "applicationReference" : "application-external-reference", "applicationId" : "palmetto-finance-application-id", "applicants" : [ { "type" : "primary", "firstName" : "Maynard", "lastName" : "Crown", "phoneNumber" : "5555550001", "address" : { "address1" : "20933 Roscoe Blvd", "city" : "Canoga Park", "state" : "MA", "zip" : "02779" } } ], "status" : "approved | approvedWithStipulations | creditFrozen | declined | expired"}Contract Events
Section titled “Contract Events”Contract Approved Event
Section titled “Contract Approved Event”This webhook is sent when a signed contract has been approved by Palmetto Finance.
{ "event" : "contractApproved", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "contractReference" : "externalReference-provided-in-contract-send", "quoteReference": "quote-external-reference"}Contract Sent Event
Section titled “Contract Sent Event”This webhook is sent when a contract has been sent to the consumer for signature.
{ "event" : "contractSent", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "contractReference" : "externalReference-provided-in-contract-send", "quoteReference": "quote-external-reference"}Contract Signed Event
Section titled “Contract Signed Event”This webhook is sent when a contract document has been digitally signed by the consumer.
{ "event" : "contractSigned", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "contractReference" : "externalReference-provided-in-contract-send", "quoteReference": "quote-external-reference"}Contract Voided Event
Section titled “Contract Voided Event”This webhook is sent when a contract is voided.
{ "event" : "contractVoided", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "contractId": "palmetto-finance-contract-id", "contractReference" : "externalReference-provided-in-contract-send", "message": "Contract has been voided", "quoteId": "palmetto-finance-associated-quote-id", "quoteReference": "quote-external-reference", "status": "voided"}Contract Reinstated Event
Section titled “Contract Reinstated Event”This webhook is sent when a voided contract is reinstated.
{ "event" : "contractReinstated", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "contractId": "palmetto-finance-contract-id", "contractReference" : "externalReference-provided-in-contract-send", "organizationId": "org-id",}Document Events
Section titled “Document Events”Document Status Changed Event
Section titled “Document Status Changed Event”This webhook is sent when the status of an Account Document changes.
{ "event" : "documentStatus", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "archived": "true | false", "documentStatus": "pending | approved | rejected | voided", "documentType": "document-type", "fileNames": [ "original-file-names" ],}Document Uploaded Event
Section titled “Document Uploaded Event”This webhook is sent when a new document is uploaded to an account.
{ "event" : "documentUploaded", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "documentId": "palmetto-finance-document-id", "documentType": "document-type", "fileNames": [ "original-file-names" ],}Milestone Package Events
Section titled “Milestone Package Events”Milestone Package Event
Section titled “Milestone Package Event”This webhook is sent whenever an action (submission, review, resubmission, approval) happens for a milestone package (install, activation, permission to operate).
{ "accountId": "palmetto-finance-account-id", "accountReference": "account-external-reference", "event": "milestonePackage", "status": "submitted", // any milestone package status (submitted, resubmitted, rejected, approved, conditionallyApproved) "timestamp": "2025-01-01", "type": "installPackage", // type of milestone package (installPackage, activationPackage, permissionToOperate) "flags": "document rejected", // comma separated list of unresolved flags}Milestone Achieved Event
Section titled “Milestone Achieved Event”This webhook is sent whenever an account has reached a major milestone (noticeToProceed, install, or activation).
{ "accountId": "66f48069ce77cd1b54fff222", "accountReference": "", "event": "milestoneAchieved", "newMilestone": "noticeToProceed", "organizationId": "my-lightreach-alias"}Milestone Status Changed Event
Section titled “Milestone Status Changed Event”This webhook is sent whenever the status of a Milestone changes.
{ "accountId": "palmetto-finance-account-id", "accountReference": "account-external-reference", "event": "milestoneStatusChanged", "milestoneType": "milestone-type", "newStatus": "approved | conditionallyApproved | paused | pending | rejected | restarted | resubmitted | submitted", "previousStatus": "approved | conditionallyApproved | paused | pending | rejected | restarted | resubmitted | submitted"}Quote Events
Section titled “Quote Events”Quote Created Event
Section titled “Quote Created Event”This webhook is sent whenever a quote has been created.
{ "event" : "quoteCreated", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "quoteId" : "palmetto-finance-quote-id", "quoteReference" : "", "status" : "active", "message" : "A new quote has been created", "contractId": "corresponding-contract-id", "contractReference": "corresponding-contract-external-reference"}Active Quote Exceeds Monthly Payment Cap
Section titled “Active Quote Exceeds Monthly Payment Cap”This webhook is sent when a quote is created for an account that exceeds their monthly payment cap
{ "event": "activeQuoteExceedsMonthlyPaymentCaps", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "monthlyPaymentCap": { "zeroEscalationRate": 50000, "greaterThanZeroEscalationRate": 50000, }, "activeQuoteId": "palmetto-finance-quote-id"}Quote Voided Event
Section titled “Quote Voided Event”This webhook is sent whenever an ordered quote has been voided.
{ "event" : "quoteVoided", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "quoteId" : "palmetto-finance-quote-id", "quoteReference" : "", "status" : "voided", "message" : "The quote has been voided"}Milestone Requirement Events
Section titled “Milestone Requirement Events”Requirement Status Changed Event
Section titled “Requirement Status Changed Event”This webhook is sent whenever the status of one or more related requirements change.
{ "accountId": "66f48069ce77cd1b54fff222", "accountReference": "", "event": "requirementStatusChanged", "requirementStatusUpdates": [ { "requirementType": "requirement-type", "newStatus": "completed | error | inProgress | pending | rejected | resubmitted | submitted", "previousStatus": "completed | error | inProgress | pending | rejected | resubmitted | submitted", }, ],}Terms and Conditions
Section titled “Terms and Conditions”Terms and Conditions Accepted
Section titled “Terms and Conditions Accepted”This webhook is sent whenever an account in Puerto Rico accepts the LightReach terms and conditions.
{ "event" : "termsAndConditionsAccepted", "accountId" : "palmetto-finance-account-id", "accountReference" : "account-external-reference", "doeReferenceId" : "doe-reference-id", "dateAccepted" : "date-time-accepted"
}