Skip to main content
POST
https://api.dcycle.io
/
api
/
v1
/
bulk
/
waste_water_treatments
Bulk Upload Waste Water Treatments
const options = {
  method: 'POST',
  headers: {
    'x-api-key': '<x-api-key>',
    'x-organization-id': '<x-organization-id>',
    'x-user-id': '<x-user-id>',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({facility_id: '<string>', daily_waste_water_treatment_data: [{}]})
};

fetch('https://api.dcycle.io/api/v1/bulk/waste_water_treatments', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));

Bulk Upload Waste Water Treatments

Upload daily waste water treatment measurements in bulk for a waste water facility. This endpoint automatically imputes missing values using interpolation and moving averages.
Important Constraints:
  • Maximum 31 days of data per request
  • The last entry must have complete data for all totalized fields
  • Missing values are automatically imputed
  • CH4 emissions cannot be negative

Request

Headers

x-api-key
string
required
Your API key for authenticationExample: sk_live_1234567890abcdef
x-organization-id
string
required
Your organization UUIDExample: ff4adcc7-8172-45fe-9cf1-e90a6de53aa9
x-user-id
string
required
Your user UUIDExample: a1b2c3d4-e5f6-7890-abcd-ef1234567890

Body Parameters

facility_id
string
required
UUID of the waste water facilityExample: "facility-uuid-here"
daily_waste_water_treatment_data
array
required
Array of daily measurement records (max 31 days)Each daily record contains:
  • name (string, required): Description of the data
  • measurement_date (date, required): Date of measurement (YYYY-MM-DD)
  • m3_water_in (float, optional): Input flow in m³
  • m3_water_out (float, optional): Output flow in m³
  • kg_bod_per_m3_wwt_line (float, optional): Input BOD in kg/m³
  • kg_n_per_m3_wwt_line (float, optional): Nitrogen in kg/m³ (treatment line)
  • kg_sludge (float, optional): Total evacuated sludge in kg
  • st (float, optional): Proportion of solids (0-1)
  • m3_biogas_engine (float, optional): Engine biogas in m³
  • m3_biogas_flare (float, optional): Flare biogas in m³
  • m3_biogas_boiler (float, optional): Boiler biogas in m³
  • methane (float, optional): Methane proportion (0-1)
  • kg_bod_per_kg_sludge_line (float, optional): BOD in sludge kg/kg
  • kg_bod_per_m3_wwd_line (float, optional): BOD in discharge kg/m³
  • kg_n_per_m3_wwd_line (float, optional): Nitrogen in discharge kg/m³
Last Entry Requirement: The last entry in your data array must have non-null values for:
  • m3_water_in
  • m3_water_out
  • kg_sludge
  • m3_biogas_engine
  • m3_biogas_flare
  • m3_biogas_boiler

Response

Returns an array of created waste water treatment records with calculated emissions.
[
  {
    "id": "treatment-uuid",
    "name": "Waste Water Treatment Data Line 1",
    "m3_water_in": 39245.33,
    "m3_water_out": 31859.12,
    "kg_sludge": 27140.0,
    "kg_bod_per_m3_wwt_line": 0.185,
    "kg_bod_per_kg_sludge_line": 0.012,
    "kg_n_per_m3_wwt_line": 0.0912,
    "kg_r_wwt_line": 2896.5,
    "file_url": null,
    "start_date": "2024-03-31",
    "end_date": "2024-03-31",
    "status": "uploaded",
    "uploaded_by": "user-uuid",
    "facility_id": "facility-uuid"
  }
]

Example

curl -X POST "https://api.dcycle.io/api/v1/bulk/waste_water_treatments" \
  -H "Authorization: Bearer ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "x-user-id: ${DCYCLE_USER_ID}" \
  -H "Content-Type: application/json" \
  -d '{
    "facility_id": "facility-uuid",
    "daily_waste_water_treatment_data": [
      {
        "name": "Daily Treatment 2024-03-01",
        "measurement_date": "2024-03-01",
        "m3_water_in": 38000,
        "m3_water_out": 30000,
        "kg_bod_per_m3_wwt_line": 0.180,
        "kg_n_per_m3_wwt_line": 0.090,
        "kg_sludge": 25000,
        "st": 0.24,
        "m3_biogas_engine": 6000,
        "m3_biogas_flare": 1200,
        "m3_biogas_boiler": 60,
        "methane": 0.6,
        "kg_bod_per_kg_sludge_line": 0.012,
        "kg_bod_per_m3_wwd_line": 0.007,
        "kg_n_per_m3_wwd_line": 0.009
      },
      {
        "name": "Daily Treatment 2024-03-02",
        "measurement_date": "2024-03-02",
        "m3_water_in": 39000,
        "m3_water_out": 31000,
        "kg_bod_per_m3_wwt_line": 0.185,
        "kg_n_per_m3_wwt_line": 0.091,
        "kg_sludge": 26000,
        "st": 0.24,
        "m3_biogas_engine": 6100,
        "m3_biogas_flare": 1220,
        "m3_biogas_boiler": 65,
        "methane": 0.6,
        "kg_bod_per_kg_sludge_line": 0.012,
        "kg_bod_per_m3_wwd_line": 0.007,
        "kg_n_per_m3_wwd_line": 0.0091
      }
    ]
  }'

Data Imputation

The endpoint automatically fills missing values using these methods:

Interpolation

Missing values are interpolated linearly between known values for:
  • m3_water_in
  • m3_water_out
  • kg_sludge
  • m3_biogas_engine
  • m3_biogas_flare
  • m3_biogas_boiler

Moving Average

Missing values are filled with 6-period moving average (forward/backward fill) for:
  • kg_n_per_m3_wwt_line
  • kg_bod_per_m3_wwt_line
  • kg_bod_per_m3_wwd_line
  • st (solid proportion)
  • methane
  • kg_bod_per_kg_sludge_line
  • kg_n_per_m3_wwd_line
You can submit partial data for intermediate days. The endpoint will automatically fill gaps, but the last entry must have complete totalized values.

Common Errors

400 Bad Request - Facility Not Found

{
  "detail": "FACILITY_NOT_FOUND",
  "code": "BAD_REQUEST"
}
Solution: Verify the facility ID exists.

400 Bad Request - Maximum Rows Exceeded

{
  "detail": "MAXIMUM_ROWS_EXCEEDED",
  "code": "BAD_REQUEST"
}
Solution: Date range between min and max measurement_date must not exceed 31 days. Split into multiple requests if needed.

400 Bad Request - Null Values in Last Row

{
  "detail": "NULL_VALUES_IN_LAST_ROW",
  "code": "BAD_REQUEST"
}
Solution: Ensure the last entry has non-null values for all totalized fields: m3_water_in, m3_water_out, kg_sludge, m3_biogas_engine, m3_biogas_flare, m3_biogas_boiler.

400 Bad Request - Negative Value

{
  "detail": "NEGATIVE_m3_water_in",
  "code": "BAD_REQUEST"
}
Solution: All measurement values must be non-negative (≥ 0).

Best Practices

1. Submit Complete Final Entry

Always ensure the last entry has all required fields:
# ✅ Good: Last entry is complete
daily_data = [
    {
        "measurement_date": "2024-03-01",
        "m3_water_in": None,  # Will be imputed
        "m3_water_out": None,  # Will be imputed
        # ... other fields
    },
    {
        "measurement_date": "2024-03-02",
        "m3_water_in": 39000,  # Complete
        "m3_water_out": 31000,  # Complete
        "kg_sludge": 26000,  # Complete
        "m3_biogas_engine": 6100,  # Complete
        "m3_biogas_flare": 1220,  # Complete
        "m3_biogas_boiler": 65,  # Complete
        # ... all required fields present
    }
]

2. Handle Date Ranges

Split large date ranges into multiple requests:
def upload_monthly_data(facility_id, monthly_data):
    # Split into chunks of max 31 days
    chunk_size = 30
    for i in range(0, len(monthly_data), chunk_size):
        chunk = monthly_data[i:i + chunk_size]

        response = requests.post(
            "https://api.dcycle.io/api/v1/bulk/waste_water_treatments",
            headers=headers,
            json={
                "facility_id": facility_id,
                "daily_waste_water_treatment_data": chunk
            }
        )

        print(f"Uploaded chunk {i//chunk_size + 1}: {len(chunk)} days")

3. Validate Before Upload

Check data quality before submitting:
def validate_wwt_data(data):
    # Check last entry completeness
    last_entry = data[-1]
    required_fields = ['m3_water_in', 'm3_water_out', 'kg_sludge',
                      'm3_biogas_engine', 'm3_biogas_flare', 'm3_biogas_boiler']

    for field in required_fields:
        if last_entry.get(field) is None:
            raise ValueError(f"Last entry missing required field: {field}")

    # Check date range
    dates = [entry['measurement_date'] for entry in data]
    date_range = (max(dates) - min(dates)).days + 1

    if date_range > 31:
        raise ValueError(f"Date range ({date_range} days) exceeds maximum of 31 days")

    return True

# Use before upload
if validate_wwt_data(daily_data):
    response = requests.post(url, headers=headers, json=request_data)