Partner API reference

HTTPS JSON on the same origin as the portal, paths under /v1. Authorization: Bearer <api_key> (pcv_…), scope read. Errors may set X-Request-ID (= error.request_id). Older keys may use read:pumps / read:stats only.

Rate limits (partner v1)

Partner API (v1)

Root-relative URLs. Access = pumps visible to portal users bound to your integration. JSON may omit undefined keys.

GET /v1/pumps

Query page, limit. Paginated controllers; statistics is fleet-wide for the integration.

/v1/pumps?page=1&limit=50
Example: 200 response (trimmed)
{
  "api_version": "1",
  "controllers": [
    {
      "pump_id": "pump-001",
      "custom_name": "North field",
      "is_owner": 1,
      "last_seen": "2025-03-20T10:00:00.000Z",
      "last_heartbeat": "2025-03-20T10:00:00.000Z",
      "installation": { "installation_name": "Main", "location_name": "Site A", "lat": 52.0, "lon": 5.0 },
      "pump": { "status": "running", "total_water_delivered": 15420.5, "running": true },
      "battery": { "voltage": 12.6, "soc": 85, "current": -2.3 },
      "communication_method": "MQTT",
      "current_pressure": 4.2,
      "active_alarms_count": 0,
      "highest_alarm_level": null,
      "total_sessions": 12,
      "uptime": 86400,
      "profile": "IRRIGATION"
    }
  ],
  "statistics": {
    "total_controllers": 1,
    "active_controllers": 1,
    "offline_controllers": 0,
    "total_water_delivered": 15420.5,
    "average_battery_soc": 85,
    "wireguard_connections": 0
  },
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "pages": 1,
    "has_next": false,
    "has_prev": false
  }
}

GET /v1/stats

statistics only (same object as GET /v1/pumps).

/v1/stats
Example: 200 response
{
  "api_version": "1",
  "statistics": {
    "total_controllers": 12,
    "active_controllers": 10,
    "offline_controllers": 2,
    "total_water_delivered": 15420.5,
    "average_battery_soc": 82.5,
    "wireguard_connections": 3
  }
}

GET /v1/delegated-users

One row per delegated user (aggregates for that user’s pumps only). Each row includes user_id and portal display name (may be null). Email is never returned.

/v1/delegated-users
Example: 200 response (shape)
{
  "api_version": "1",
  "user_identity_policy": "user_id_and_name",
  "delegated_users": [
    {
      "user_id": 101,
      "name": "Acme Farms",
      "total_controllers": 3,
      "active_controllers": 2,
      "offline_controllers": 1,
      "total_water_delivered": 4200.0,
      "average_battery_soc": 78.3,
      "wireguard_connections": 1
    }
  ]
}

GET /v1/users/:userId/summary

Per delegated user: same statistics and paginated controllers shape as GET /v1/pumps, but only that user’s pumps (page, limit). This is not the time-series payload of GET /v1/pumps/:pumpId/summary.

/v1/users/101/summary?page=1&limit=50
Example: 200 response (trimmed)
{
  "api_version": "1",
  "user_id": 101,
  "name": "Acme Farms",
  "statistics": {
    "total_controllers": 1,
    "active_controllers": 1,
    "offline_controllers": 0,
    "total_water_delivered": 15420.5,
    "average_battery_soc": 85,
    "wireguard_connections": 0
  },
  "controllers": [
    { "pump_id": "pump-001", "custom_name": "North field", "is_owner": 1 }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "pages": 1,
    "has_next": false,
    "has_prev": false
  }
}

GET /v1/pumps/:pumpId/metadata

Persisted controller metadata.

/v1/pumps/PUMP_ID/metadata
Example: 200 response (shape)
{
  "api_version": "1",
  "pump_id": "pump-001",
  "metadata": {
    "pump_id": "pump-001",
    "installation_name": "Main",
    "location_name": "Site A",
    "latitude": 52.0,
    "longitude": 5.0,
    "last_seen": "2025-03-20T10:00:00.000Z",
    "total_water_delivered": 15420.5
  }
}

More metadata fields may appear over time.

GET /v1/pumps/:pumpId/summary

Live fields, today, alarms, week trends, lifetime aggregates.

/v1/pumps/PUMP_ID/summary
Example: 200 response (trimmed)
{
  "api_version": "1",
  "pump_id": "pump-001",
  "metadata": { "pump_id": "pump-001" },
  "live_data": { "current_pressure": null, "battery_soc": 84, "battery_voltage": 12.5, "last_updated": "…" },
  "today": { "date": "2025-03-20", "statistics": { }, "snapshot_count": 0, "is_today": true },
  "recent_alarms": { "count": 0, "critical": 0, "warning": 0, "alarms": [] },
  "week_trends": { "daily_summaries": [], "total_water_delivered": "0.0", "avg_pressure": null, "avg_battery_soc": null, "avg_uptime": "0" },
  "total_average_water": { "avg_daily_water": 0, "total_water": 0, "days_with_data": 0, "calculation_method": "no_data" },
  "lifetime_averages": { "avg_pressure": null, "avg_battery_soc": null, "calculation_method": "no_data" },
  "last_updated": "2025-03-20T10:00:00.000Z",
  "data_source": "database"
}

Errors (partner v1)

Body shape:

{
  "error": {
    "code": "invalid_api_key",
    "message": "Human-readable detail",
    "request_id": "…"
  }
}
HTTPerror.codeTypical cause
400invalid_paginationBad page / limit on GET /v1/pumps or GET /v1/users/:userId/summary.
400invalid_user_idNon-numeric userId in GET /v1/users/:userId/summary.
401unauthorizedMissing or bad Bearer token.
401invalid_api_keyUnknown or revoked key.
403no_delegated_usersNo portal users on this integration.
403insufficient_scopeKey lacks read scope.
403forbiddenNo access to that pumpId, or userId is not a delegated user on this integration (or inactive).
404not_foundNo metadata/summary for that pump.
429rate_limitedToo many requests; see Retry-After.
500internal_errorAuth lookup failed.