Wildvine BDS API Reference

Endpoint reference for the Biodiversity Dynamics Simulation (BDS) module + related v3 endpoints used by the Explore page.

Base URL: https://api.wildvine.kotaksakti.com

Quick reference

Auth: all endpoints sit behind Cognito (same as the main app). Hit them from the FE with the user's bearer token. Hectare options: 2 (Pasoh, location_id=1) or 50 (the other plot, location_id=2).

GET/v3/bds/compartments

Static list of valid compartment sizes for the Hectare dropdown.

Parameters

None.

Example request

curl "https://api.wildvine.kotaksakti.com/v3/bds/compartments"

Example response

{
  "data": [
    { "compartment_hectare": 2,  "location_id": 1 },
    { "compartment_hectare": 50, "location_id": 2 }
  ]
}

GET/v3/bds/years

Available census years for a given compartment. Use this to populate the Year dropdown so it never offers a year with no data.

Parameters

NameTypeRequiredDescription
compartment_hectareintegerrequired2 or 50

Example request

curl "https://api.wildvine.kotaksakti.com/v3/bds/years?compartment_hectare=50"

Example response

{
  "data": [1986, 1990, 1995, 2000, 2005, 2011],
  "compartment_hectare": 50
}
50ha has census data through 2011; 2ha through 2021. The UI's 2025 mock isn't backed by real data — restrict the dropdown to what this endpoint returns.

Error response

{ "detail": "compartment_hectare=99 is not valid. Valid options: [2, 50]" }

GET/v3/bds/simulation

Full data for BDS Page 1 (Pre-Felling): tree-density metrics with comparison percentages, distribution charts, and Candidate Trees summary + per-species breakdown.

Parameters

NameTypeRequiredDescription
yearintegerrequiredCensus year, e.g. 2021
compartment_hectareintegerrequired2 or 50

Example request

curl "https://api.wildvine.kotaksakti.com/v3/bds/simulation?year=2021&compartment_hectare=50"

Response structure

{
  "pre_felling": {
    "tree_density":               { "value": integer, "change_pct": integer },
    "tree_density_per_ha":        { "value": float,   "change_pct": integer },
    "volume_m3_per_ha":           { "value": float,   "change_pct": integer },
    "basal_area_m2_per_ha":       { "value": float,   "change_pct": integer },
    "total_biomass_t":             { "value": float,   "change_pct": integer },
    "total_carbon_t":              { "value": float,   "change_pct": integer },
    "dipterocarp_density_per_ha":  { "value": float,   "change_pct": integer }
  },
  "distributions": {
    "species_group_density": [
      { "group": "Non-dipterocarp", "value": integer }
    ],
    "dbh_size_class_density": [
      { "class": "30-45cm" | "45-60cm" | "60-75cm" | "75-90cm" | ">90cm", "value": integer }
    ]
  },
  "candidate_trees": {
    "summary": {
      "tree_density":        integer,
      "tree_density_per_ha": float,
      "volume_m3":           float,
      "basal_area_m2":       float,
      "total_biomass_t":     float,
      "total_carbon_t":      float,
      "price_rm":            float
    },
    "species_breakdown": [
      {
        "species_group":           "Dipterocarp" | "Non-dipterocarp" | "Chengal",
        "cutting_limit_cm":        float,
        "tree_density":            integer,
        "tree_density_per_ha":     float,
        "volume_m3":               float,
        "volume_m3_per_ha":        float,
        "basal_area_m2":           float,
        "basal_area_m2_per_ha":    float,
        "total_biomass_t":         float,
        "total_biomass_t_per_ha":  float,
        "total_carbon_t":          float,
        "total_carbon_t_per_ha":   float
      }
    ]
  },
  "meta": {
    "compartment_hectare": integer,
    "year":                integer,
    "updated_at":          "2026-06-04T08:00:00Z"
  }
}

Example response (abbreviated, 50ha 2021)

{
  "pre_felling": {
    "tree_density":               { "value": 3750,   "change_pct": 91 },
    "tree_density_per_ha":        { "value": 75.0,   "change_pct": 91 },
    "volume_m3_per_ha":           { "value": 138.52, "change_pct": 86 },
    "basal_area_m2_per_ha":       { "value": 15.26,  "change_pct": 49 },
    "total_biomass_t":             { "value": 267.46, "change_pct": 63 },
    "total_carbon_t":              { "value": 125.71, "change_pct": 63 },
    "dipterocarp_density_per_ha":  { "value": 21.1,   "change_pct": 28 }
  },
  "distributions": {
    "species_group_density": [
      { "group": "Non-dipterocarp", "value": 2631 },
      { "group": "Dipterocarp",     "value": 983 },
      { "group": "Chengal",         "value": 72 },
      { "group": "Pioneer",         "value": 64 }
    ],
    "dbh_size_class_density": [
      { "class": "30-45cm", "value": 2307 },
      { "class": "45-60cm", "value": 794 },
      { "class": "60-75cm", "value": 320 },
      { "class": "75-90cm", "value": 167 },
      { "class": ">90cm",   "value": 162 }
    ]
  },
  "candidate_trees": {
    "summary": {
      "tree_density": 700, "tree_density_per_ha": 14.0,
      "volume_m3": 4255.64, "basal_area_m2": 366.22,
      "total_biomass_t": 6323.56, "total_carbon_t": 2972.07,
      "price_rm": 365386.78
    },
    "species_breakdown": [
      { "species_group": "Dipterocarp",     "cutting_limit_cm": 65, "tree_density": 262, "...": "..." },
      { "species_group": "Non-dipterocarp", "cutting_limit_cm": 55, "tree_density": 396, "...": "..." },
      { "species_group": "Chengal",         "cutting_limit_cm": 70, "tree_density": 42,  "...": "..." }
    ]
  },
  "meta": { "compartment_hectare": 50, "year": 2021, "updated_at": "2026-06-04T08:00:00Z" }
}
change_pct = percentage of the PF metric (DBH≥30cm) relative to all trees in the dataset. E.g. tree_density.change_pct = 91 means DBH≥30 trees are 91% of all trees.

GET/v3/bds/auto-selection

Auto Calculate Tree Selection — returns all three regimes (Heavy / Medium / Light) in one response. Each regime block has the harvest list, totals row, Immediate Implication (residual stand), Logging Damage breakdown, and the 30-year Future Implication chart data.

Parameters

NameTypeRequiredDescription
yearintegerrequiredCensus year, e.g. 2021. Get valid values from /v3/bds/years.
compartment_hectareintegerrequired2 or 50

Business rules

RuleValue
Heavy regime target20 trees/ha × compartment_hectare
Medium regime target16 trees/ha × compartment_hectare
Light regime target12 trees/ha × compartment_hectare
Selection orderCandidate trees sorted by DBH descending; take top N
Logging damage (+60cm)20% of remaining trees in class removed
Logging damage (45-60cm)30% removed
Logging damage (30-45cm)40% removed
Logging damage (15-30cm)50% removed (skipped in practice — PF residual is DBH≥30)
LD selection within classSmallest-DBH trees dropped first
Future projection30 years, every 5 years (6 data points)

Example request

curl "https://api.wildvine.kotaksakti.com/v3/bds/auto-selection?year=2021&compartment_hectare=2"

Response structure

{
  "regimes": {
    "heavy":  RegimeBlock,   // 20 trees/ha
    "medium": RegimeBlock,   // 16 trees/ha
    "light":  RegimeBlock    // 12 trees/ha
  },
  "meta": {
    "compartment_hectare": integer,
    "year":                integer,
    "updated_at":          "ISO-8601 string"
  }
}

RegimeBlock = {
  "config": {
    "regime":             "heavy" | "medium" | "light",
    "trees_per_ha":       integer,   // 20 / 16 / 12
    "target_total":       integer,   // trees_per_ha * compartment_hectare
    "actually_selected":  integer    // capped by candidate pool size
  },

  // ─── Per-tree harvest list (drives the table on the Auto page) ───
  "selected_trees": [
    {
      "tag":             string,
      "scientific_name": string,
      "species_group":   string,
      "dbh_cm":          float,
      "basal_area_m2":   float,
      "volume_m3":       float,
      "biomass_t":       float,
      "carbon_tC":       float,
      "price_rm":        float
    }
  ],

  // ─── Totals row at the bottom of selected_trees ───
  "selected_total": {
    "tree_count":               integer,
    "basal_area_m2":            float,
    "basal_area_m2_per_ha":     float,
    "volume_m3":                float,
    "volume_m3_per_ha":         float,
    "total_biomass_t":          float,
    "total_biomass_t_per_ha":   float,
    "total_carbon_tC":          float,
    "total_carbon_tC_per_ha":   float,
    "price_rm":                 float
  },

  // ─── Right-side "Immediate Implication / Residual Stand" panel ───
  "residual_stand": {
    "tree_density":             integer,
    "tree_density_per_ha":      float,
    "volume_m3_per_ha":         float,
    "basal_area_m2_per_ha":     float,
    "total_biomass_t_per_ha":   float,
    "total_carbon_tC_per_ha":   float
  },

  // ─── Logging Damage breakdown (diagnostic) ───
  "logging_damage": {
    "by_dbh_class": [
      { "class": "+60cm",   "removed_pct": 20, "trees_removed": integer },
      { "class": "45-60cm", "removed_pct": 30, "trees_removed": integer },
      { "class": "30-45cm", "removed_pct": 40, "trees_removed": integer },
      { "class": "15-30cm", "removed_pct": 50, "trees_removed": integer }
    ],
    "total_trees_removed": integer
  },

  // ─── "Future Implication" 5-year line charts (next 30 years) ───
  "future_implication": {
    "years":                integer[6],   // [year+5, +10, +15, +20, +25, +30]
    "tree_density_per_ha":  float[6],
    "volume_m3_per_ha":     float[6],
    "basal_area_m2_per_ha": float[6],
    "biomass_t_per_ha":     float[6],
    "carbon_tC_per_ha":     float[6]
  }
}

Example response (Heavy regime only, 2ha 2021)

{
  "regimes": {
    "heavy": {
      "config": {
        "regime": "heavy",
        "trees_per_ha": 20,
        "target_total": 40,
        "actually_selected": 32
      },
      "selected_trees": [
        {
          "tag": "3010",
          "scientific_name": "Koompassia malaccensis",
          "species_group": "Non-dipterocarp",
          "dbh_cm": 119.6,
          "basal_area_m2": 1.12359,
          "volume_m3": 14.60669,
          "biomass_t": 24.12313,
          "carbon_tC": 11.33787,
          "price_rm": 464.83
        },
        {
          "tag": "2378",
          "scientific_name": "Sarcotheca griffithii",
          "species_group": "Non-dipterocarp",
          "dbh_cm": 112.4,
          "basal_area_m2": 0.99238,
          "volume_m3": 12.90096,
          "biomass_t": 20.82428,
          "carbon_tC": 9.78741,
          "price_rm": 414.83
        }
        // ... up to actually_selected rows
      ],
      "selected_total": {
        "tree_count": 32,
        "basal_area_m2": 15.39787,
        "basal_area_m2_per_ha": 7.69893,
        "volume_m3": 176.38323,
        "volume_m3_per_ha": 88.19161,
        "total_biomass_t": 288.74247,
        "total_biomass_t_per_ha": 144.37124,
        "total_carbon_tC": 135.70896,
        "total_carbon_tC_per_ha": 67.85448,
        "price_rm": 16026.44
      },
      "residual_stand": {
        "tree_density": 97,
        "tree_density_per_ha": 48.5,
        "volume_m3_per_ha": 58.1,
        "basal_area_m2_per_ha": 7.9,
        "total_biomass_t_per_ha": 122.31,
        "total_carbon_tC_per_ha": 57.49
      },
      "logging_damage": {
        "by_dbh_class": [
          { "class": "+60cm",   "removed_pct": 20, "trees_removed": 4 },
          { "class": "45-60cm", "removed_pct": 30, "trees_removed": 12 },
          { "class": "30-45cm", "removed_pct": 40, "trees_removed": 42 },
          { "class": "15-30cm", "removed_pct": 50, "trees_removed": 0 }
        ],
        "total_trees_removed": 58
      },
      "future_implication": {
        "years":                [2026, 2031, 2036, 2041, 2046, 2051],
        "tree_density_per_ha":  [43.55, 39.16, 35.63, 33.00, 31.14, 29.90],
        "volume_m3_per_ha":     [61.20, 63.80, 66.10, 68.00, 69.50, 70.70],
        "basal_area_m2_per_ha": [8.57, 8.62, 8.70, 8.81, 8.93, 9.01],
        "biomass_t_per_ha":     [130.20, 135.40, 139.80, 143.50, 146.70, 149.20],
        "carbon_tC_per_ha":     [61.20, 63.60, 65.70, 67.40, 68.90, 70.10]
      }
    },
    "medium": { /* same shape, 16 trees/ha target */ },
    "light":  { /* same shape, 12 trees/ha target */ }
  },
  "meta": {
    "compartment_hectare": 2,
    "year": 2021,
    "updated_at": "2026-06-04T08:00:00Z"
  }
}
actually_selected < target_total means the candidate pool was smaller than the regime's target. E.g. 50ha 2011 has only 44 candidate trees — Heavy wants 1000, gets 44. UI may want to surface this so users know the harvest is pool-limited.

GET/v3/flora

Paginated per-tree records for the Explore page. Supports filtering by year, location, species, DBH class, and protection status.

Parameters

NameTypeRequiredDescription
yearsinteger[]optionale.g. ?years=2017&years=2021
location_idsinteger[]optional1 = 2ha plot, 2 = 50ha plot
species_idsstring[]optionalSpecies codes from species.csv
species_groupsstring[]optionalDipterocarp, Non-dipterocarp, Chengal, Pioneer
dbh_size_classesstring[]optional0_15, 15_30, 30_45, 45_60, 60_75, 75_90, 90
protected_treestring[]optionaltrue / false (or 1 / 0). Binary only — no IUCN categories.
scientific_namestring[]optionalPartial match on scientific name
pageintegeroptional1-based page number (default 1)
page_sizeintegeroptionalDefault 10, max 5000

Example request

curl "https://api.wildvine.kotaksakti.com/v3/flora?years=2021&location_ids=2&dbh_size_classes=30_45&protected_tree=true&page_size=20"

Response structure

{
  "data": [
    {
      "tag":             string,
      "species":         string,           // species code
      "species_name":    string,
      "species_group":   string,
      "redlist":         "0" | "1",        // 1 = protected
      "timber_group":    string,
      "quad":            string,
      "xco":             float, "yco": float,
      "longitude":       float, "latitude": float,
      "year":            integer,
      "data":            float,            // DBH in cm
      "dbh_size_class":  string,
      "extrapolated":    bool,
      "basal_area":      float,
      "height":          float,
      "volume":          float,
      "agb":             float,
      "bgb":             float,
      "biomass":         float,
      "carbon_stock":    float,
      "price_rm":        float,
      "location_id":     integer
    }
  ],
  "pagination": {
    "page":         integer,
    "page_size":    integer,
    "total":        integer,
    "total_pages":  integer,
    "has_next":     bool,
    "has_prev":     bool
  },
  "params": {
    "years":            "2021",
    "species_groups":   null,
    "location_ids":     "2",
    "species_ids":      null,
    "dbh_size_classes": "30_45",
    "protected_tree":   "true",
    "scientific_name":  null
  }
}
protected_tree: backend only stores binary 0/1 (not IUCN categories like CR/EN/VU). Sending true returns trees with redlist=1; false returns redlist=0.

Notes

Last updated 2026-06-04 · Hosted on Cloudflare Pages