Skip to content

Getting Started

This is a basic description of the steps needed to submit a project for booking to the Energy API. For more detailed information, please refer to the API documentation.

If you are using TypeScript we recommend downloading our project template which makes it quick and easy to get up and running on this API. This template includes a generated client SDK with rich typing to make navigating and calling the API simple.

View Project Template

You need to acquire an access token. This token should be cached and used for the lifetime of that token. This can be determined by using the exp claim in the token itself. See the environment guide for information and authUrl and baseUrl.

import fetch from 'node-fetch';
const response = await fetch(`${authUrl}`, {
method: 'POST',
body: JSON.stringify({
username: 'string',
password: 'string',
}),
headers: { 'Content-Type': 'application/json' },
});
const accessToken = await response.json();
access_token;

Palmetto offers a suite of webhooks that allow you to be alerted to status changes to your projects automatically. For more information, refer to the subscription documentation.

Reference: Subscription API

Get the Palmetto service area for a location

Section titled “Get the Palmetto service area for a location”

Check to see if Palmetto services the area and the utility. Make a call to the service area endpoint, passing in latitude, longitude, and postal code, to get a list of utilities that Palmetto supports.

import fetch from 'node-fetch';
const postalCode = '02908';
const lat = '41.85205806726778';
const lon = '-71.43945851226846';
const response = await fetch(`${envBaseUrl}/api/service-area?lat=${lat}&lon=${lon}&postalCode=${postalCode}`, {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${accessToken}`,
},
});
const serviceAreaUtilities = await response.json();
{
name: 'Rhode Island',
coordinates: { lat: 41.601439, lon: -71.46297087752808 },
supportedUtilities: [
...,
{
name: 'Rhode Island Energy (formerly National Grid - Rhode Island)',
lseId: 507,
rates: [
...,
{
name: "Residential",
masterTariffId: 859,
tariffId: 3445715,
availability: {
availableFrom: "2023-07-01T00:00:00.000Z",
available: true
}
},
...
]
},
...
]
}

Reference: Service Areas API

When submitting a project you need to list the components that are being installed by their SKUs. You can get this list from our products endpoint. If you use Aurora for your designs, we have included an attribute on each product with the key auroraId that you can use to match a component in aurora to the SKU in our system. You should consider caching this list for an extended period of time as the product list does not change frequently.

import fetch from 'node-fetch';
const response = await fetch(`${envBaseUrl}/api/products`, {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${accessToken}`,
},
});
const products = (await response.json()).data;
const mapping = products
?.map((p) => ({
sku: p.sku,
name: p.name,
auroraId: p.attributes?.find((a) => a.key === 'auroraId')?.value,
}))
.filter((p) => p.auroraId);
{
data: [
...,
{
id: "63f2898ad2d53c9e6465a656",
sku: "canadianSolar-cs3n395ms",
name: "Canadian Solar CS3N-395MS 395w",
manufacturer: "Canadian Solar",
shortDescription: "395W All Black Module",
longDescription: "Canadian Solar CS3N-395MS, 395W BOB 66C 35MM",
type: "simple",
classificationId: "63f27056245f32be8c8f64a3",
availability: {
available: true
},
restrictions: {
financiers: [],
states: [],
systemSize: []
},
attributes: [
{
key: "auroraId",
value: "c0095004-109c-4feb-8813-0fbaab1107bd"
}
]
},
...
]
}

Reference: Products API

Once you are ready to submit a project, for palmetto to fulfill, the first step is to create a project. You can think of a project as a container for all of the information about the system that you are submitting. This includes the design, estimates, documents, etc. You may create a project with the minimum information required see the schema and update it as the customer progresses through your system, or you may create a project with all of the information. This affords you the flexibility to support your business process.

import fetch from 'node-fetch';
const body = {
name: 'My Project',
type: 'SOLAR_RESIDENTIAL',
externalId: 'external-id-123',
address: {
streetAddress: '20933 Roscoe Blvd',
locality: 'Canoga Park',
region: 'MA',
postalCode: '02779',
country: 'US',
coordinates: {
lat: -71.0952744,
lon: 41.8718583,
},
},
contacts: [
{
tags: ['OWNER'],
firstName: 'Maynard',
middleName: 'Percy',
lastName: 'Crown',
phones: [
{
tags: ['MOBILE'],
number: '+18048675309',
},
],
locale: 'en-US',
},
],
design: {
provider: 'AURORA',
providerReferenceId: 'aurora-id',
tsrf: 0.85,
firstYearProductionKwh: 7409,
components: [
{
name: 'Enphase IQ 8 Plus Microinverter',
sku: 'iq8-plus',
quantity: 1,
},
{
name: 'Canadian Solar CS3N-395MS 395w',
sku: 'canadianSolar-cs3n395ms',
quantity: 13,
},
],
arrays: [
{
sizeW: 400,
moduleQuantity: 1,
tilt: 14,
azimuth: 221,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 3,
tilt: 14,
azimuth: 221,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 2,
tilt: 14,
azimuth: 131,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 3,
tilt: 14,
azimuth: 131,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 4,
tilt: 14,
azimuth: 41,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
],
},
estimate: {
dealerFee: 0.25,
lineItems: [
{
name: 'Solar System',
sku: 'pv-residential',
quantity: 5200,
units: 'WATT',
unitNetPrice: 3.6,
netPrice: 18720,
},
{
name: 'Palmetto Protect Essentials',
sku: 'palmetto-protect-essentials',
quantity: 1,
units: 'OTHER',
unitNetPrice: 150,
netPrice: 150,
},
],
},
paymentMethods: [
{
type: 'PPA',
amount: 18720,
provider: 'PALMETTO',
providerReferenceId: '64a822dd2238174defd6b09c',
},
],
};
const response = await fetch(`${envBaseUrl}/api/projects`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(body),
});
const project = await response.json();
{
status: 'DRAFT',
id: '64cd33c08697c006a821fccc',
name: 'My Project',
type: 'SOLAR_RESIDENTIAL',
contacts: [
{
firstName: 'Maynard',
middleName: 'Percy',
lastName: 'Crown',
phones: [
{
tags: ['MOBILE'],
number: '+18048675309',
},
],
locale: 'en-US',
id: '64cd33c08697c006a821fccd',
tags: ['OWNER']
}
],
address: {
streetAddress: '20933 Roscoe Blvd',
locality: 'Canoga Park',
region: 'MA',
postalCode: '02779',
country: 'US',
coordinates: { lat: -71.0952744, lon: 41.8718583 }
},
design: {
id: '64cd33c08697c006a821fcce',
arrays: [
{
sizeW: 400,
moduleQuantity: 1,
tilt: 14,
azimuth: 221,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 3,
tilt: 14,
azimuth: 221,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 2,
tilt: 14,
azimuth: 131,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 3,
tilt: 14,
azimuth: 131,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
{
sizeW: 400,
moduleQuantity: 4,
tilt: 14,
azimuth: 41,
tsrf: 0,
systemLosses: 0,
firstYearProductionKwh: 0,
},
],
components: [
{
name: 'Enphase IQ 8 Plus Microinverter',
sku: 'iq8-plus',
quantity: 1,
},
{
name: 'Canadian Solar CS3N-395MS 395w',
sku: 'canadianSolar-cs3n395ms',
quantity: 13,
},
],
firstYearProductionKwh: 7409,
provider: 'AURORA',
providerReferenceId: 'aurora-id',
tsrf: 0.85
},
estimate: {
dealerFee: 0.25
"totalAdjustments": [
{
"name": "Dealer fee",
"isCalculated": true,
"rate": 0.25,
"amount": 10450.16,
"type": "DEALER_FEE"
}
]
"totalNetPrice": 19326,
"totalGrossPrice": 29776.16,
lineItems: [
{
"sku": "pv-residential",
"name": "Solar System",
"quantity": 5200,
"units": "WATT",
"netPrice": 18720,
"grossPrice": 29018.75,
"unitNetPrice": 3.6,
"unitGrossPrice": 5.5805,
"adjustments": [
{
"name": "Dealer fee",
"isCalculated": true,
"rate": 0.25,
"amount": 10298.75,
"type": "DEALER_FEE"
}
]
},
{
"name": "Palmetto Protect Essentials",
"sku": "palmetto-protect-essentials",
"quantity": 1,
"units": "OTHER",
"netPrice": 150,
"grossPrice": 187.48,
"unitNetPrice": 150,
"unitGrossPrice": 187.48,
"adjustments": [
{
"name": "Dealer fee",
"isCalculated": true,
"rate": 0.25,
"amount": 37.47999999999999,
"type": "DEALER_FEE"
}
]
}
]
},
paymentMethods: [
{
type: 'PPA',
amount: 18720,
provider: 'PALMETTO',
providerReferenceId: '64a822dd2238174defd6b09c'
}
],
documents: [],
owner: { teamId: '5e9dd0997f5ed3003b24fc77', userId: 3753 }
}

Reference: Project Create API

You can fully submit a project via the portal. You can add a project from the Projects list using the + Project button:

AddProjectButton

The New project form accepts all of the same data required via the API, broken into sections. To start, give your project a name and a reference identifier (this is usually an id that you use internally to identify a project).

NewProjectForm

The Contacts section gathers all of the contact information for the homeowners. You can add another contact using the + Add button. You may need additional contacts if there is a coborrower for the loan, or if the utility account holder is not the primary contact. You can make use of Tags to indicate the roles of each contact.

ContactsSection

The Address section captures the address of the property where the system is being installed. You’ll need the latitude and longitude of the property. You can get this via google maps, or by using the geography API.

AddressSection

The Design section serves to collect details about the solar system.

DesignSection

Each design consists of components. These are the inverters, panels, and so on that comprise the solar system. You can find the various components that Palmetto supports using the Products API.

ComponentsSection

You’ll also need to add the solar arrays that comprise the solar system design. You can add multiple arrays with the + Add button.

SolarArraysSection

The Estimate section outlines all of the pricing and line items that make up the project, including the solar system itself and any add-ons. You can add as many line items as you wish with the + Add button.

EstimateSection

The Payment methods section captures details about the financing for the project. You will need to provide a reference id for whichever financier is financing the project. You can add multiple payment methods if need be using the + Add button.

FinancierReference Id
PALMETTOAccount Id
GOODLEAPLoan Id
ROCKETLoan Id

PaymentMethodsSection

When all of the fields have been filled out, use the Save project button to save your project. If there are any issues with the data entered, those will appear underneath the field with the error.

ProjectFormError

After a project is created you will need to upload documents. There are three types of required document types, DESIGN, HIC, and UTILITY_BILL, and you must have at least one of each type that has at least one file. Each Document is made up of one or more files, that are uploaded using a multipart/form-data request. There is also an additional MISCELLANEOUS type that can be used to upload any additional supporting documents.

import fetch from 'node-fetch';
import FormData from 'form-data';
import fs from 'fs';
const data = FormData();
data.append('type', 'HIC');
data.append('file[1]', fs.createReadStream('file.jpg'));
data.append('file[2]', fs.createReadStream('anotherFile.jpg'));
const response = await fetch(`${envBaseUrl}/api/projects/${projectId}/documents`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
},
body: data,
});
const result = await response.json();
{
id: '64d9aa7a3eb77429f9e4e902',
type: 'HIC',
archived: false,
files: [
{
contentType: 'image/jpeg',
originalName: 'file.jpg',
md5Hash: 'nZwbJzLOngS+2QHrRXs7/w==',
sizeKB: 6067.8,
viewUrls: [{
type: 'ORIGINAL',
URL: '...'
}]
}
]
}

Reference: Project Documents API

You can manage uploads for a project under the Documents section. To upload a document, select the Upload button. DocumentsSection Select the Document Type for the document you’re uploading, and choose the files. You may upload the documents as a single file with multiple pages, or as individual files.

DocumentsUploadModal

The uploaded files will display under the Documents section. You can view the status and the files you uploaded for each document type.

DocumentSectionPostUpload

Once you have created a project, added all the required information, and uploaded the supporting documents, you can submit the project for acceptance.

import fetch from 'node-fetch';
const response = await fetch(`${envBaseUrl}/api/projects/${projectId}/submission`, {
method: 'PUT',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${accessToken}`,
},
});
const result = await response.json();
{
"status": "PENDING",
"submittedAt": "2023-08-05T22:11:39.181Z"
}

Reference: Project Submit API

You can submit a project in the portal using the Submit Project button at the top of the project detail page.

ProjectDetailPageTop

If there are any issues with the project you are submitting, an error will display with the problem areas. You can address the issues and try submitting again.

ProjectDetailSubmissionError

After a project has been submitted our team will review it and choose to either accept or reject the project. Projects are rejected for a variety of reasons but some examples would be that is is not a viable solar system or the address is outside of a service area. If your project is rejected you can make changes and then re-submit the project.

You will be notified via webhook events or you may also choose to poll using the project status endpoint.