Supplier Webhooks
Our webhook system allows third‑party Suppliers to receive real‑time notifications about key events in our platform. By subscribing to these webhooks, your systems can react immediately to important events such as new user connections, orders placed, and changes to supplier quotes.
Authentication
Requests must include a bearer token in the HTTP Authorization header.
Supplier Event Webhook (User Connected / Order Placed)
The event webhook URL is set on the linked distributor resource. It receives user_connected and order_placed events.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| PUT | /api/orgs/:org_id/linked_distributor | Set webhook URL |
| GET | /api/orgs/:org_id/linked_distributor | Get webhook URL |
:org_id- your OpenSolar Organization ID
Configuring Webhook URL
Request Body
{
"event_webhook_url": "<url>"
}
Success Response — 200 OK
{
"id": "<string>",
"org": "<url>",
"org_name": "<string>",
"filter_key": "<string>",
"name": "<string>",
"countries": "<country_code>",
"is_enabled": true,
"event_webhook_url": "<url>",
"logo_url": null,
"description": "<string>",
"privacy_policy_url": "<url>",
"terms_of_use_url": "<url>"
}
Note: Privacy Policy and Terms of Use links can be updated via the Configuration page under Suppliers in your Control area on the OpenSolar platform.
Webhook Events
User Connected
Triggered when a user submits their details to your Organization via the OpenSolar platform.
Fields Returned
| Field | Type | Description |
|---|---|---|
email | string | User's email address. |
phone | string | User's phone number. |
first_name | string | User's given name. |
family_name | string | User's surname. |
job_title | string | User's job title. |
org_name | string | Name of the Organization the user belongs to. |
org_id | string | Unique identifier of the Organization. |
address | string | Street address of the Organization. |
zip | string | Postal/ZIP code of the Organization's address. |
state | string | State or region of the Organization's address. |
Example Payload
{
"event": "user_connected",
"details": {
"email": "susan.rays@example.com",
"phone": "+1-555-987-6543",
"first_name": "Susan",
"family_name": "Rays",
"job_title": "Sales Manager",
"org_name": "Acme Solar Solutions",
"org_id": "5678",
"address": "456 Green Energy Blvd",
"zip": "90210",
"state": "CA"
}
}
Order Placed
Triggered when a customer places a new order.
Fields Returned
| Field | Type | Description |
|---|---|---|
org_id | integer | OpenSolar's unique identifier of the Organization. |
order_id | integer | OpenSolar's unique identifier generated for the order. |
seller_org_reference | string | The Supplier's identifier of the Organization if set. |
preferred_supplier_location | string | Default location set for the supplier. |
total_items | integer | Number of items in the order. |
items | array | List of items in the order. |
subtotal_price | string | Total price before tax. |
total_price | string | Total price including tax. |
total_tax | string | Total tax amount. Omitted for US orders (see note below). |
project_ids | array | Related project IDs, if any. |
shipping_info | object | Shipping and delivery details. |
contact_info | object | Delivery contact details. |
supplier_id | integer | Unique organization identifier of the Supplier. |
discounts | object | null | Deals and discounts applied to the order, or null if none. See Deals & Discounts. |
shipping_cost | object | null | Shipping cost details, or null if none. |
For orders placed in the United States:
subtotal_priceandtotal_pricewill always be the same value, as sales tax is not calculated.- The
total_taxfield will not be included in the payload.
shipping_info Object
| Field | Type | Description |
|---|---|---|
payment_option | string | Selected payment method for the order (e.g. "cash", "credit"). |
delivery_date | string | Scheduled date for delivery or collection. |
delivery_time | string (optional) | Preferred delivery time from checkout, HH:MM:SS±HH:MM when set. |
collection_location | string (optional) | Chosen pickup location when delivery method is collection. |
delivery_instructions | string | Additional instructions provided for delivery. |
delivery_method | string | Method of fulfillment: "delivery" or "collection". |
address | object | Delivery address details for the order. |
Note: The
collection_locationfield is optional, and will be visible only when the delivery method is Collection and a Supplier has multiple locations defined.
discounts Object
Contains information about discounts applied to the order, including the currency, discount applications, and the allocation of discount amounts. For Deal types, Supplier Assist, and worked examples, see Deals & Discounts.
| Field | Type | Description |
|---|---|---|
currency | string | The ISO currency code in which discount amounts are expressed. |
total_discount_amount | string | The total value of all discounts applied to the order. |
discount_applications | array | A list of discounts applied to the order. See Discount application below. Each application has a corresponding allocation (see Discount allocation below). |
discount_allocations | array | A list showing how each discount is allocated to line items or the order. See Discount allocation below. Each allocation references a discount application (see above). |
Each item in discount_applications has the following fields:
| Field | Type | Description |
|---|---|---|
id | string | A unique identifier for the discount application. |
source | string | Identifies the system or mechanism that created or provided the discount. |
type | string | The type of discount, such as percentage or fixed_amount. |
title | string | A short, user-facing name for the discount. |
description | string | null | An optional description providing additional information about the discount. |
target | string | Specifies what the discount applies to, such as line_item or order. |
value | object | Defines the value of the discount. See value object below. |
priority | integer | null | Determines the order in which applicable discounts are processed. Lower values indicate higher priority. |
value object (within each discount application)
| Field | Type | Description |
|---|---|---|
kind | string | Specifies how the discount value is interpreted, such as percentage or fixed_amount. |
amount | string | The discount value. For a percentage discount, this represents the percentage; for a fixed amount, it represents the monetary amount. |
Each item in discount_allocations has the following fields:
| Field | Type | Description |
|---|---|---|
discount_application_id | string | References the id of the discount application responsible for the allocation. |
target | string | Specifies what the allocation applies to, such as line_item or order. |
sku | string | null | The SKU of the line item receiving the discount. null when the discount applies to the entire order. |
amount | string | The monetary amount of the discount allocated to the specified target. |
shipping_cost Object
| Field | Type | Description |
|---|---|---|
label | string | null | Display label for the shipping method (e.g. "Standard Delivery"). |
cost | number | null | The shipping cost amount in currency. |
Example Payload
{
"event": "order_placed",
"details": {
"org_id": 1018,
"order_id": 1234,
"seller_org_reference": 123,
"preferred_supplier_location": "nottingham_warehouse",
"supplier_id": 1056,
"total_items": 1,
"subtotal_price": "20.00",
"total_tax": "4.00",
"total_price": "24.00",
"project_ids": [],
"items": [
{
"quantity": 1,
"title": "Aiko Neostar 2S 455W N0Type ABC, 54 Cell, Mono Glass All Black, Gen 2",
"product_code": "AIKO-A455-MAH54Mb/2S",
"total_price": "20.00",
"distributor_product_code": "AIKO-A455-MAH54Mb/2S"
}
],
"shipping_info": {
"payment_option": "cash",
"delivery_date": "07/31/2025",
"delivery_time": "09:30:00-07:00",
"delivery_instructions": "Deliver to doorstep",
"delivery_method": "delivery",
"address": {
"address": "123 Solar Drive",
"locality": "Los Angeles",
"state": "CA",
"zip": "90210",
"country_name": "United States of America"
}
},
"contact_info": {
"email": "susanrays@solartest.com",
"phone_number": "+1-555-555-1234",
"first_name": "Susan",
"last_name": "Rays",
"org_name": "Solar Inc"
},
"discounts": {
"currency": "GBP",
"total_discount_amount": "48.00",
"discount_applications": [
{
"id": "deal:3",
"source": "deal",
"type": "percentage",
"title": "10% off",
"description": "10% off on Panels",
"target": "line_item",
"value": {
"kind": "percentage",
"amount": "10.00"
},
"priority": 1
},
{
"id": "deal:1",
"source": "deal",
"type": "percentage",
"title": "Bundle deal",
"description": "Bundle deal",
"target": "line_item",
"value": {
"kind": "percentage",
"amount": "20.00"
},
"priority": 10
}
],
"discount_allocations": [
{
"discount_application_id": "deal:3",
"target": "line_item",
"sku": "635654",
"amount": "10.00"
},
{
"discount_application_id": "deal:1",
"target": "line_item",
"sku": "635654",
"amount": "18.00"
},
{
"discount_application_id": "deal:1",
"target": "line_item",
"sku": "654995",
"amount": "10.00"
}
]
},
"shipping_cost": {
"label": "Standard Delivery",
"cost": 25.0
}
}
}
(Optional) Redirect User After Order Is Placed
The order_placed webhook supports an optional redirect feature that can send customers to a custom URL after checkout.
The checkout_url must be returned in the JSON body of the same HTTP 200 OK response sent to acknowledge the webhook.
Example Response:
{
"checkout_url": "https://yourstore.com/thank-you"
}
When a valid checkout_url is provided, customers are redirected to this location after order completion. This can be used to redirect to a payment processing flow, a custom confirmation page, your order portal, and more.
Quote and Quote Activity Log Webhooks
Suppliers can subscribe to real-time webhooks for Quote and Quote Activity Log changes. These use the platform's standard webhook infrastructure (documented in the API webhooks section) but are configured through a dedicated distributor endpoint rather than the general webhooks API. Quote webhooks fire on CREATE, UPDATE, and DELETE events; Quote Activity Log webhooks fire on CREATE and DELETE only (activity logs are immutable).
- Scope is fixed — you subscribe to
Quoteand/orQuoteActivityLogas a whole. There is no per-field control overtrigger_fieldsorpayload_fields; both are alwaysquote.*and/orquoteactivitylog.*. - Configuration via UI or distributor API — these webhooks can be managed through the Seller Configuration UI in OpenSolar or via the distributor webhook API below.
- Org targeting — webhooks fire to the supplier org when its org is the supplier on the quote.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/orgs/:org_id/distributor_webhooks | List quote-related webhooks |
| POST | /api/orgs/:org_id/distributor_webhooks | Create or update a webhook |
| DELETE | /api/orgs/:org_id/distributor_webhooks?endpoint=<url> | Delete a webhook by endpoint |
:org_id- your OpenSolar Organization ID- Only admin users can manage webhooks.
Creating a webhook
Request Body
{
"endpoint": "https://yourapp.com/webhooks/opensolar",
"headers": {
"Authorization": "Bearer your-secret-token"
},
"webhook_types": ["Quote", "QuoteActivityLog"],
"enabled": true
}
| Field | Type | Required | Description |
|---|---|---|---|
endpoint | string (URL) | Yes | The URL that will receive webhook POST requests. |
headers | object | null | No | Custom HTTP headers sent with each webhook request (e.g. auth tokens). |
webhook_types | array of strings | Yes | One or both of "Quote" and "QuoteActivityLog". At least one is required. |
enabled | boolean | No | Whether the webhook is active. Defaults to true. |
Response — 201 Created
{
"endpoint": "https://yourapp.com/webhooks/opensolar",
"headers": {
"Authorization": "Bearer your-secret-token"
},
"webhook_types": ["Quote", "QuoteActivityLog"],
"enabled": true
}
Updating an existing webhook
To update an existing webhook, POST with an original_endpoint field alongside the new values.
{
"original_endpoint": "https://yourapp.com/webhooks/opensolar",
"endpoint": "https://yourapp.com/webhooks/v2",
"headers": {
"Authorization": "Bearer new-token"
},
"webhook_types": ["Quote"],
"enabled": true
}
Deleting a webhook
DELETE /api/orgs/:org_id/distributor_webhooks?endpoint=https://yourapp.com/webhooks/opensolar
Response — 204 No Content
Webhook types
Quote
Fires when a quote is created, updated, or deleted. The payload follows the standard webhook structure with model set to "Quote". See Quote webhook payload for the full field reference.
QuoteActivityLog
Fires when activity is logged on a quote — messages, file uploads, and system events such as status changes. Activity logs are immutable, so only CREATE and DELETE events are fired (never UPDATE). The payload has model set to "QuoteActivityLog". See Quote Activity Log webhook payload for the full field reference.