Skip to content

Webhooks

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.

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:

  1. Server API key
  2. Client headers
  3. Client basic authorization header

An overview of each follows. For more detailed implementation information, see the API Reference.

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.

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.

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}.

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.

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 nonretryableErrorCodes list

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.

For a full list of events, see the schema in the API reference.

event nameDescription
accountCreatednotifies when a new account is created
accountUpdatedany updates to an account
activeQuoteExceedsMonthlyPaymentCapsquote for account exceeds monthly payment cap
allEventssubscribe to all events at this path
allConsumerTaskEventssubscribe to all consumer events at this path
allContractEventssubscribe to all contract events at this path
allStipulationEventssubscribe to all stipulation events at this path
allStipulationsClearedall stipulations have been cleared
applicationStatusany application status changes
contractApprovedany contract approved
contractReinstateda previously voided contract is reinstated
contractSentany contract sent
contractSignedany contract signed
contractVoidedany contract voided
documentStatusany change to the status of an uploaded document
documentUploadednotifies you when a new document is uploaded
illinoisShineDisclosureSentIllinois Shines disclosure sent
illinoisShineDisclosureSignedIllinois Shines disclosure signed
milestoneAchievedany milestone reached
milestonePackageevents for milestone packages
milestoneStatusChangedevents for changes to milestone statuses
quoteCreatedany quote created
quoteVoidedany quote voided
requirementStatusChangedany change to an account requirement status
requirementCompletedany account requirement completed
stipulationAdded.a single stipulation added
stipulationCleareda single stipulation cleared
termsAndConditionsAcceptedterms and conditions accepted

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",
}

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"
}

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",
"email" : "[email protected]",
"address" : {
"address1" : "20933 Roscoe Blvd",
"city" : "Canoga Park",
"state" : "MA",
"zip" : "02779"
}
}
],
"status" : "approved | approvedWithStipulations | creditFrozen | declined | expired"
}

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"
}

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"
}

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"
}

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"
}

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",
}

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" ],
}

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" ],
}

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
}

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"
}

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"
}

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"
}

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"
}

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"
}

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",
},
],
}

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"
}