Skip to main content

Webhooks

Flow Access API uses webhooks to notify your system when important events occur in real time.

Instead of polling our system for updates, we push event data directly to your server whenever something happens — such as a meter sending uplinks, a recharge being completed, or water being fetched.

Webhooks are asynchronous and event-driven.


Polling vs Webhooks

Polling

  • Your system repeatedly asks Flow for updates
  • Increased latency and unnecessary requests
  • Not suitable for real-time events

Webhooks

  • Flow sends data only when something happens
  • Near real-time delivery
  • Lower infrastructure and network cost

How Webhooks Work

The webhook delivery flow follows these steps:

  1. Register a Webhook URL When creating or configuring an API client, you provide a webhookURL.

  2. An Event Occurs Examples include:

  • A meter sends uplink data
  • A recharge invoice is created or completed
  • A water fetch is recorded
  1. Flow Sends a Webhook Flow sends an HTTP POST request with a JSON payload to your webhook URL.

  2. Your Server Acknowledges Your server must respond with HTTP 201 Created to confirm successful processing.


Important

If your webhook endpoint:

  • Returns any status code other than 201 Created, or
  • Takes longer than 10 seconds to respond

the webhook delivery is considered failed.


When Webhooks Are Sent

Webhooks are sent automatically when Flow processes events from its internal event bus.

You do not need to call any API to trigger webhooks.

Internally, Flow listens to system events and forwards them to the correct client based on:

  • Business ID
  • Event data type
  • Webhook activation status

Supported Event Types

Each webhook event has a specific purpose and payload structure.


Types of Events

Here are the webhook events currently sent by Flow. We may add more events as we hook into additional actions in the future.

EventDescription
device_logA device (meter) has sent uplinks
recharges_logA recharge, invoice has been created, succeeded, or failed
fetches_logA water fetch transaction has been recorded

This event is sent whenever a meter or device sends uplinks data to Flow.

Payload Structure

{
"battery1Alarm": false,
"battery2Alarm": false,
"eeAlarm": false,
"emptyTubeAlarm": false,
"flowRate": 1.23,
"overRangeAlarm": false,
"pipeLeakageAlarm": false,
"pipeBurstAlarm": false,
"receivedAt": "2025-12-15T10:00:00Z",
"deviceTime": "2025-12-15T09:59:58Z",
"rechargeBalance": 1234.5,
"rechargeTimes": 10,
"reverseFlowAlarm": false,
"address": "device-serial-number-123",
"totalConsumption": 54321.9,
"valveStatus": 1,
"waterTemperature": 25.5,
"waterTemperatureAlarm": false,
"valveMode": 1,
"closeThreshold": 100,
"lastPurchaseCredit": 50.0
}

Field Descriptions

FieldTypeDescription
battery1AlarmbooleanAlarm status for battery 1.
battery2AlarmbooleanAlarm status for battery 2.
eeAlarmbooleanEEPROM alarm.
emptyTubeAlarmbooleanAlarm for an empty tube.
flowRatenumberCurrent flow rate of water.
overRangeAlarmbooleanAlarm for when the flow is over range.
pipeLeakageAlarmbooleanAlarm for a detected pipe leakage.
pipeBurstAlarmbooleanAlarm for a detected pipe burst.
receivedAtstringTimestamp (ISO 8601) when the server received the data.
deviceTimestringTimestamp (ISO 8601) from the device itself.
rechargeBalancenumberThe current remaining water credit balance.
rechargeTimesintegerThe total number of times the device has been recharged.
reverseFlowAlarmbooleanAlarm for water flowing in the reverse direction.
addressstringThe unique serial number or identifier for the device.
totalConsumptionnumberTotal water consumption recorded by the device.
valveStatusnumberThe current status of the valve.
waterTemperaturenumberThe temperature of the water.
waterTemperatureAlarmbooleanAlarm for water temperature exceeding limits.
valveModeintegerThe operational mode of the valve.
closeThresholdintegerThe threshold at which the valve will close.
lastPurchaseCreditnumberThe amount of the last credit purchase.

Recharge Receipt (recharges_log)

This feed provides information about sales transactions, specifically when a user recharges their water credit balance.

Payload Structure

{
"id": "recharge-id-456",
"amount": 20.0,
"litres": 2000.0,
"pricePerLitre": 0.01,
"currency": "USD",
"paymentMode": "MOBILE_MONEY",
"paymentType": "PREPAID",
"status": "COMPLETED",
"reason": "",
"serialNumber": "device-serial-number-123",
"deviceType": "METER_V1",
"volume": 2000,
"device": {
"id": "device-id-789",
"name": "Main Street Meter",
"serialNumber": "device-serial-number-123",
"status": "ACTIVE",
"category": "RESIDENTIAL",
"coordinate": {
"latitude": -1.286389,
"longitude": 36.817223
},
"zone": {
"id": "zone-id-abc",
"name": "Downtown",
"location": "Nairobi",
"currency": "KES",
"pricePerLitre": 1.2,
"supportPhoneNumber": "+254700000000"
}
},
"card": {
"id": "card-id-xyz",
"serialNumber": "card-serial-12345",
"type": "NFC_CARD",
"linkedAt": "2025-12-15T10:00:00Z",
"createdAt": "2025-12-15T10:00:00Z",
"active": true,
"customer": {
"id": "user-id-def",
"firstName": "Jane",
"lastName": "Doe",
"username": "janedoe",
"phoneNumber": "+254712345678",
"emailAddress": "jane.doe@example.com"
}
},
"user": {
"id": "user-id-def",
"firstName": "Jane",
"lastName": "Doe",
"username": "janedoe",
"phoneNumber": "+254712345678",
"emailAddress": "jane.doe@example.com"
},
"performedBy": {
"id": "user-id-ghi",
"firstName": "John",
"lastName": "Smith",
"username": "johnsmith",
"phoneNumber": "+254787654321",
"emailAddress": "john.smith@example.com"
},
"completedAt": "2025-12-15T11:30:00Z",
"createdAt": "2025-12-15T11:29:00Z",
"zone": {
"id": "zone-id-abc",
"name": "Downtown",
"location": "Nairobi",
"currency": "KES",
"pricePerLitre": 1.2,
"supportPhoneNumber": "+254700000000"
},
"type": "RECHARGE"
}

Field Descriptions

FieldTypeDescription
idstringThe unique ID for this recharge transaction.
amountnumberThe monetary amount of the recharge.
litresnumberThe number of litres of water credited.
pricePerLitrenumberThe price per litre at the time of the transaction.
currencystringThe currency of the transaction.
paymentModestringHow the payment was made (e.g., "MOBILE_MONEY", "CARD").
paymentTypestringThe type of payment (e.g., "PREPAID").
statusstringThe status of the transaction (e.g., "COMPLETED").
deviceobjectDetails about the device.
cardobjectDetails about the card used.
userobjectThe user who checks the account.
performedByobjectThe user who performed the recharge.
completedAtstringTimestamp (ISO 8601) when the transaction was completed.
createdAtstringTimestamp (ISO 8601) when the transaction was created.
zoneobjectDetails about the zone.
typestringThe type of transaction.

Water Fetch Receipt (fetches_log)

This feed provides a receipt every time water is fetched from a device.

Payload Structure

{
"id": "receipt-id-789",
"type": "FETCH",
"user": {
"id": "user-id-def",
"firstName": "Jane",
"lastName": "Doe",
"username": "janedoe",
"phoneNumber": "+254712345678",
"emailAddress": "jane.doe@example.com"
},
"serialNumber": "device-serial-number-123",
"litres": 15.5,
"pricePerLiter": 0.01,
"amount": 0.155,
"createdAt": "2025-12-15T12:00:00Z",
"zone": {
"id": "zone-id-abc",
"name": "Downtown",
"location": "Nairobi",
"currency": "KES",
"pricePerLitre": 1.2,
"supportPhoneNumber": "+254700000000"
},
"device": {
"id": "device-id-789",
"name": "Main Street Meter",
"serialNumber": "device-serial-number-123",
"status": "ACTIVE",
"category": "RESIDENTIAL",
"coordinate": {
"latitude": -1.286389,
"longitude": 36.817223
},
"zone": {
"id": "zone-id-abc",
"name": "Downtown",
"location": "Nairobi",
"currency": "KES",
"pricePerLitre": 1.2,
"supportPhoneNumber": "+254700000000"
}
},
"card": {
"id": "card-id-xyz",
"serialNumber": "card-serial-12345",
"type": "NFC_CARD",
"linkedAt": "2025-12-15T10:00:00Z",
"createdAt": "2025-12-15T10:00:00Z",
"active": true,
"customer": {
"id": "user-id-def",
"firstName": "Jane",
"lastName": "Doe",
"username": "janedoe",
"phoneNumber": "+254712345678",
"emailAddress": "jane.doe@example.com"
}
},
"balance": 1219.0,
"businessId": "business-id-123"
}

Field Descriptions

FieldTypeDescription
idstringThe unique ID for this water fetch receipt.
typestringThe type of event (e.g., "FETCH").
userobjectThe user who fetched the water.
serialNumberstringThe serial number of the device.
litresnumberThe amount of water fetched in litres.
pricePerLiternumberThe price per litre at the time of fetching.
amountnumberThe total cost of the water fetched.
createdAtstringTimestamp (ISO 8601) when the water was fetched.
zoneobjectThe zone where the fetch occurred.
deviceobjectThe device used for fetching.
cardobjectThe card used for fetching.
balancenumberThe remaining balance on the account.
businessIdstringThe ID of the business associated with this transaction.

Handling Webhooks Correctly

Your webhook endpoint should:

  1. Accept POST requests
  2. Parse JSON payloads
  3. Process the event idempotently
  4. Respond within 10 seconds

Example Integration

func HandleWebhook(c echo.Context) error {
// ... (see api.md for full struct definitions)
return c.NoContent(http.StatusCreated)
}