External API Documentation
API key–based integration grants You access to Calendar, Projects, Clients, and Components. This gives you flexibility to integrate with your systems and automate processes.
Table of Contents
- Authentication
- Pagination
- Calendar API
- Projects API
- Clients API
- Components API
- Data Conventions
- Filtering Behavior
- Permission Model
- Error Handling
- Integration Patterns
- Stability & Versioning
1. Authentication
All requests must include an API key in the request header.
Authorization: Api-Key <raw_api_key>
Notes: - API keys are created and managed by company owners and admins. - Each key is scoped to specific resource permissions. - All requests are automatically scoped to the company attached to the API key.
Authentication Errors
| Status | Reason |
|---|---|
401 | Missing or invalid API key |
401 | Malformed authorization header |
401 | Inactive API key |
403 | Missing resource permission |
402 | Company inactive |
2. Pagination
List endpoints (Calendar, Projects, Clients) use limit/offset pagination.
Parameters
| Parameter | Description | Default | Max |
|---|---|---|---|
limit | Number of results to return | 100 | 500 |
offset | Pagination offset | 0 | — |
Response Envelope
{
"count": 123,
"next": null,
"previous": null,
"results": []
}
When next is null, you have reached the last page.
3. Calendar API
Represents scheduled events such as meetings, calls, and installations. Read-only.
Endpoints
GET https://api-production.easysolar-app.com/integrations/calendar/
GET https://api-production.easysolar-app.com/integrations/calendar/{id}/
Required permission: calendar_read
Response Example
{
"id": "7d0f4d55-3b66-4c71-a86d-2b0ef2fca6d5",
"category": "meeting",
"subject": "Initial Site Visit",
"description": "Discuss installation requirements and roof inspection.",
"client": {
"id": "4ab8e7cb-0a27-48f1-a0f6-6d6ecf5d8c6a",
"name": "Solar Corp",
"address": "Main Street 10, Warsaw",
"phone": "+48123456789",
"is_open": true
},
"participants": [
{
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@example.com"
}
],
"start_at": "2026-06-15T09:00:00Z",
"end_at": "2026-06-15T10:00:00Z",
"full_day": false,
"created_at": "2026-06-01T12:30:45Z"
}
Field Reference
| Field | Type | Description |
|---|---|---|
id | UUID | Event identifier |
category | string | Event category (see values below) |
subject | string | Event title |
description | string | Additional event notes |
client | object | null | Linked client |
participants | array | Employees assigned to the event |
start_at | datetime | Event start time (UTC) |
end_at | datetime | Event end time (UTC) |
full_day | boolean | Whether the event spans the entire day |
created_at | datetime | Event creation timestamp |
Category Values
| Value | Description |
|---|---|
meeting | Meeting |
call | Phone call |
installation | Installation appointment |
contract | Contract signing |
quotation | Quotation presentation |
Nested Objects
Client object:
| Field | Type | Description |
|---|---|---|
id | UUID | Client identifier |
name | string | Client name |
address | string | Client address |
phone | string | Client phone number |
is_open | boolean | Client active status |
Participant object:
| Field | Type | Description |
|---|---|---|
first_name | string | Employee first name |
last_name | string | Employee last name |
email | string | Employee email address |
Filters
| Parameter | Type | Description |
|---|---|---|
start_at_after | datetime | Filter events starting after this time |
start_at_before | datetime | Filter events starting before this time |
end_at_after | datetime | Filter events ending after this time |
end_at_before | datetime | Filter events ending before this time |
category | string (multi) | Filter by category; repeat for multiple values |
client | UUID | Filter by client ID |
Example:
GET https://api-production.easysolar-app.com/integrations/calendar/?start_at_after=2026-01-01T00:00:00Z&category=meeting&category=call
Authorization: Api-Key <key>
4. Projects API
Represents client-related project records (e.g. solar installations). Read-only.
Endpoints
GET https://api-production.easysolar-app.com/integrations/projects/
GET https://api-production.easysolar-app.com/integrations/projects/{id}/
Required permission: projects_read
Response Example
{
"id": "d8e22cb9-1d88-4a3d-a3fd-f954d1f23d59",
"name": "Solar Installation - Warsaw Office",
"address": "Aleje Jerozolimskie 120, Warsaw",
"latitude": 52.2297,
"longitude": 21.0122,
"client": {
"id": "4ab8e7cb-0a27-48f1-a0f6-6d6ecf5d8c6a",
"name": "Solar Corp",
"address": "Main Street 10, Warsaw",
"phone": "+48123456789",
"is_open": true
},
"status": {
"name": "In Progress",
"order": 2
},
"currency": {
"code": "PLN",
"name": "Polish Złoty"
},
"total_price": "42500.00",
"payback_period": 8,
"created": "2026-05-01T09:00:00Z",
"updated": "2026-06-01T15:30:00Z"
}
Field Reference
| Field | Type | Description |
|---|---|---|
id | UUID | Project identifier |
name | string | Project name |
address | string | Project address |
latitude | decimal | null | Project latitude |
longitude | decimal | null | Project longitude |
client | object | Linked client (see Client object) |
status | object | Project status |
currency | object | Currency used for financial values |
total_price | decimal string | Current project total price |
payback_period | integer | null | Estimated payback period in years |
created | datetime | Creation timestamp |
updated | datetime | Last update timestamp |
total_priceis returned as a decimal string to preserve precision.payback_periodisnullif not yet calculated.latitude/longitudearenullif no location is set.
Nested Objects
Status object:
| Field | Type | Description |
|---|---|---|
name | string | Status display name |
order | integer | Status ordering value |
Currency object:
| Field | Type | Description |
|---|---|---|
code | string | ISO currency code |
name | string | Currency name |
Filters
| Parameter | Type | Description |
|---|---|---|
created_after | datetime | Filter by creation date (lower bound) |
created_before | datetime | Filter by creation date (upper bound) |
updated_after | datetime | Filter by update date (lower bound) |
updated_before | datetime | Filter by update date (upper bound) |
client | UUID | Filter by client ID |
status | UUID | Filter by status ID |
name | string | Case-insensitive partial name match |
Example:
GET https://api-production.easysolar-app.com/integrations/projects/?client=<id>&name=solar
Authorization: Api-Key <key>
5. Clients API
Represents customer entities. Supports read and write operations.
Endpoints
GET https://api-production.easysolar-app.com/integrations/clients/
POST https://api-production.easysolar-app.com/integrations/clients/
GET https://api-production.easysolar-app.com/integrations/clients/{id}/
PATCH https://api-production.easysolar-app.com/integrations/clients/{id}/
Required permissions: clients_read (read), clients_write (write)
Response Example
{
"id": "4ab8e7cb-0a27-48f1-a0f6-6d6ecf5d8c6a",
"name": "Solar Corp",
"description": "Commercial customer interested in a rooftop PV installation.",
"address": "Main Street 10, Warsaw",
"latitude": 52.2297,
"longitude": 21.0122,
"phone": "+48123456789",
"is_open": true,
"created": "2026-05-01T10:15:00Z",
"updated": "2026-06-01T08:45:30Z"
}
Field Reference
| Field | Type | Description |
|---|---|---|
id | UUID | Client identifier |
name | string | Client name |
description | string | Additional notes |
address | string | Client address |
latitude | decimal | null | Client latitude |
longitude | decimal | null | Client longitude |
phone | string | null | Client phone number |
is_open | boolean | Whether the client is currently active |
created | datetime | Creation timestamp |
updated | datetime | Last update timestamp |
Create Client
POST https://api-production.easysolar-app.com/integrations/clients/
{
"name": "Client Name",
"description": "Commercial customer",
"address": "Address",
"phone": "+48123456789",
"latitude": 52.2297,
"longitude": 21.0122
}
Update Client
PATCH https://api-production.easysolar-app.com/integrations/clients/{id}/
Only include the fields you want to update:
{
"address": "New Address 15, Warsaw",
"phone": "+48987654321"
}
Coordinate Rules
latitudeandlongitudemust always be provided together.- Supplying only one coordinate results in a validation error.
- If no location is set, both fields are returned as
null.
Filters
| Parameter | Type | Description |
|---|---|---|
created_after | datetime | Filter by creation date (lower bound) |
created_before | datetime | Filter by creation date (upper bound) |
updated_after | datetime | Filter by update date (lower bound) |
updated_before | datetime | Filter by update date (upper bound) |
is_open | boolean | Filter by active status (true or false) |
name | string | Case-insensitive partial name match |
phone | string | Case-insensitive partial phone match |
6. Components API
Creates components for use in projects. Write-only.
Endpoint
POST https://api-production.easysolar-app.com/integrations/components/
Required permission: components_write
Component Types
Three component types are supported: panel, inverter, and other.
Panel
{
"type": "panel",
"manufacturer_name": "Longi",
"name": "LR5-54HPH",
"net_unit_price": "120.00",
"nominal_power": "430.000",
"length": "1.7220",
"width": "1.1340",
"weight": "21.500",
"efficiency": "0.2100000",
"short_circuit_current": "13.52000",
"open_circuit_voltage": "37.85000",
"current_temperature_coefficient": "0.045000",
"voltage_temperature_coefficient": "-0.280000",
"power_temperature_coefficient": "-0.350000"
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "panel" |
manufacturer_name | string | Yes | Manufacturer name |
name | string | Yes | Panel model name |
net_unit_price | decimal | No | Net unit price |
nominal_power | decimal | Yes | Rated power (W). Must be between 1.0 and 1000.0 |
length | decimal | Yes | Panel length (m). Must be between 0.1 and 5.0 |
width | decimal | Yes | Panel width (m). Must be between 0.1 and 5.0 |
weight | decimal | null | No | Weight (kg) |
efficiency | decimal | Yes | Conversion efficiency. Must be a ratio between 0 and 1 |
short_circuit_current | decimal | Yes | Short-circuit current (A). Must be between 0.00001 and 999.99999 |
open_circuit_voltage | decimal | Yes | Open-circuit voltage (V). Must be between 0.00001 and 999.99999 |
current_temperature_coefficient | decimal | null | No | Current temperature coefficient. If provided, must be between −99.999999 and 99.999999 |
voltage_temperature_coefficient | decimal | null | No | Voltage temperature coefficient |
power_temperature_coefficient | decimal | null | No | Power temperature coefficient |
Inverter
{
"type": "inverter",
"manufacturer_name": "SMA",
"name": "Sunny Tripower 5.0",
"net_unit_price": "900.00",
"nominal_power": "5000.000",
"number_of_dc_inputs": 2
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "inverter" |
manufacturer_name | string | Yes | Manufacturer name |
name | string | Yes | Inverter model name |
net_unit_price | decimal | No | Net unit price |
nominal_power | decimal | Yes | Rated power (W). Must be between 100.0 and 100000000.0 |
number_of_dc_inputs | integer | Yes | Number of DC inputs. Must be at least 1 |
Other
{
"type": "other",
"name": "Mounting System",
"unit": "pcs",
"net_unit_price": "50.00"
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be "other" |
name | string | Yes | Component name |
unit | string | No | Unit of measurement |
net_unit_price | decimal | No | Net unit price |
Response
{
"id": "uuid",
"type": "panel | inverter | other",
"name": "string"
}
Notes
- Manufacturers are automatically created if they don't exist.
- Manufacturer uniqueness is scoped per company.
- All panel-specific fields are validated before creation.
7. Data Conventions
IDs
All resource identifiers are UUID strings, stable and globally unique within a company:
"id": "550e8400-e29b-41d4-a716-446655440000"
Timestamps
All datetime fields are returned in ISO 8601 format, always in UTC:
"created": "2026-06-09T12:34:56Z"
Geolocation
Coordinates apply to Clients and Projects. They are stored as a geographic point and returned as separate fields:
"latitude": 52.2297,
"longitude": 21.0122
Null Handling
Fields that are not set are returned as null — they are never omitted from the response.
8. Filtering Behavior
General Rules
- All filters are optional unless stated otherwise.
- Multiple filters are combined with AND logic.
- Multi-select filter values (e.g.
category) use OR logic internally. - Invalid filter values return empty results or a validation error depending on field type.
Date Range Filters
Available across endpoints:
| Filter fields | Behavior |
|---|---|
created_after / created_before | Range filter on creation timestamp |
updated_after / updated_before | Range filter on last-update timestamp |
start_at_after / start_at_before | Range filter on event start time |
end_at_after / end_at_before | Range filter on event end time |
Text Search Filters
Case-insensitive partial matching on name and phone:
?name=solar
Matches: "Solar Corp", "My Solar Project", etc.
9. Permission Model
API key permissions are configured independently per resource:
| Resource | Read Permission | Write Permission |
|---|---|---|
| Calendar | calendar_read | (not supported) |
| Projects | projects_read | (not supported) |
| Clients | clients_read | clients_write |
| Components | (not supported) | components_write |
10. Error Handling
Error Format
General error:
{
"detail": "Error message"
}
Field-level validation error:
{
"field_name": ["Error message"]
}
Error Reference
| Status | Category | Meaning |
|---|---|---|
401 | Authentication | Invalid, missing, or inactive API key; malformed header |
402 | Business | Company is inactive |
403 | Authorization | API key lacks the required resource permission |
4xx | Validation | Field-level errors returned as an object |
Integration Checklist
If you encounter errors, verify:
- The API key has the correct resource permissions.
- The company account is active.
- All filter values (IDs, etc.) belong to the same company scope.
11. Integration Patterns
Sync Strategy
The recommended approach for syncing data:
- Fetch the list endpoint (paginated).
- Store the
idas an external reference. - Use the
updatedfield for incremental syncs.
Pagination Handling
Iterate through pages by incrementing the offset until next is null:
GET https://api-production.easysolar-app.com/integrations/clients/?limit=100&offset=0
GET https://api-production.easysolar-app.com/integrations/clients/?limit=100&offset=100
Incremental Sync
Use date filters to fetch only records changed since your last sync:
GET https://api-production.easysolar-app.com/integrations/projects/?updated_after=2026-01-01T00:00:00Z
Filtering Best Practices
- Prefer ID-based filters (
client,status) over text searches for high-volume syncs. - Combine multiple filters to reduce result set size.
- Avoid broad text searches on large datasets.
12. Stability & Versioning
Stable Guarantees
The following will not change without a versioned migration:
- Endpoint URLs
- Field names
- UUID format
- Pagination structure
- Authentication scheme
Subject to Change (Non-Breaking)
- Optional filters (new filters may be added)
- Response enrichment (new fields may be added)
Versioning Policy
No explicit version is currently exposed in the URL. Breaking changes will introduce a new endpoint namespace:
https://api-production.easysolar-app.com/integrations/v2/...
Additive changes are considered non-breaking and may be deployed without a version bump.
End of Documentation
