Webhooks
Webhooks allow your application to receive real-time notifications about events that occur within the Light platform. This enables you to build responsive and up-to-date integrations without the need for constant polling.
It may not be necessary to use webhooks depending on how you plan to use the Light API. However, they can be useful when you use the Account API to talk directly from your client application to the Light API, to notify your backend of key events.
Setting up webhooks
To set up webhooks for your app, please reach out to our support team. Soon, you'll be able to manage webhook endpoints directly through our developer dashboard. When setting up a webhook, you'll provide us with an API endpoint URL where we'll send webhook events. You'll also receive a secret key that you can use to verify the authenticity of webhook events as every webhook event is signed with an HMAC signature.
Supported webhooks
Currently, we support the following webhook events:
-
enrollment.plan_requested
: Triggered when a customer requests a plan- This event won't have a location set, but will have an account since the location isn't saved until the plan is accepted.
-
enrollment.plan_accepted
: Triggered when a customer accepts an electricity plan -
enrollment.identity_updated
: Triggered when a customer's identity information is updated -
enrollment.finalized
: Triggered when an enrollment is finalized -
account.payment_method_added
: Triggered when a new payment method is added to an account -
account.billing_address_updated
: Triggered when a billing address is updated -
account.payment_failed
: Triggered when a payment for an invoice fails for an account -
account.payment_successful
: Triggered when a payment for an invoice is successful for an account -
location.service_active
: Triggered when electricity service becomes active for a location -
location.service_canceled
: Triggered when electricity service is canceled for a location -
location.usage_history_available
: Triggered when usage history becomes available for a location -
comparison_invoice.processed
: Triggered when a comparison invoice is done being processed (either successfully or unsuccessfully)
Webhook payload format
Webhook payloads are sent as JSON in the body of a POST request. The root keys of all webhooks will look the same, but the data object will vary depending on the event type. Here's an example of a webhook payload:
{
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"created_at": "2023-04-01T12:00:00Z",
"api_version": "v1",
"event": "enrollment.plan_accepted",
"data": {
"account": {
"uuid": "98765432-e89b-12d3-a456-426614174000",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com"
// ... other account details
}
}
}
The enrollment
and account
events will contain an account
object mirroring that of the Account API response.
The location
events will contain a location
object mirroring that included within the account object.
Webhook delivery
We attempt to deliver webhooks up to 3 times total with a backoff if we don't receive a 2xx response from your server. All attempts will be made within a 10-minute window. If all attempts fail, the webhook will be discarded.
Best practices
- We recommend using webhooks as triggers to fetch data that would be eventually fetched either way, and not as a source of truth. Webhooks can be missed or delivered out of order due to a variety of issues from the sending or receiving side.
- Design your system to handle occasional missing webhooks or duplicate deliveries, even though these scenarios are rare.
- The webhook request will timeout after 10 seconds, so ensure your webhook endpoint can respond within that time frame. We recommend processing the webhook payload asynchronously to avoid blocking the response.
- Implement proper error handling and logging for webhook processing.
- For webhooks types that you don't need to process, respond with a 200 status code to acknowledge receipt of the webhook. Responding with a 2xx status code will prevent the webhook from being retried. In addition if we receive an abundance of 4xx or 5xx responses, we may stop sending webhooks to your endpoint in the future.
Webhook signatures
To ensure the security and authenticity of webhook payloads, we sign each webhook using HMAC with SHA-256. The signature is included in the Light-Signature-v1
header of the webhook request.
The header value is in the format: {timestamp}.{hmac}
Where:
timestamp
is the Unix timestamp to the nearest second when the webhook was senthmac
is the hexadecimal representation of the HMAC-SHA256 digest
To verify the signature, you should:
- Split the
Light-Signature-v1
header value into its timestamp and hmac components. - Verify that the timestamp is not too old (e.g., older than 1 hour) to limit replay attack exposure.
- Generate your own HMAC-SHA256 digest using the timestamp and the raw payload of the webhook separated by a period.
- Compare the resulting digest with the hmac provided in the header using a secure, constant-time comparison method.
Here's an example implementation of signature verification in Python:
import hashlib
import hmac
import time
def compare_signature(
signing_secret: str, payload: str, provided_signature: str
) -> bool:
"""
Compare the provided Light-Signature-v1 signature to the computed
expected signature using a sha256 HMAC digest.
"""
split = provided_signature.split(".")
if len(split) != 2:
return False
timestamp, hmac_digest = split
# if the timestamp is older than 1 hour, reject the request to avoid
# replay attacks
if int(timestamp) < time.time() - 3600:
return False
message_bytes = f"{timestamp}.{payload}".encode("utf-8")
expected_digest = hmac.new(
signing_secret.encode("utf-8"),
message_bytes,
hashlib.sha256,
).hexdigest()
# use a constant-time comparison to avoid timing attacks
return hmac.compare_digest(hmac_digest, expected_digest)
By implementing signature verification, you can ensure that the webhooks you receive are genuine and have not been tampered with.