⚡

Pashudhan Nutri AI API

Version 2.1 · ICAR-2013+NASEM Compliant · REST & Python SDK

The Pashudhan Nutri AI API gives your cooperative or agri-tech platform plug-and-play access to India's most accurate dairy ration engine. Send a single HTTP request and receive a complete, science-backed ration in JSON — or use the official pashudhan_ai Python SDK for a one-liner workflow.

B2B Only. This API is exclusively for Enterprises and Dairy Cooperatives. Individual farmers should use the Web Formulator.
REST
HTTP / JSON
ICAR+NASEM
2013+2021 Standards
~45-60s
Avg Response Time

Authentication

All API requests require an x-api-key header. Get your key from the API Dashboard. Treat it like a password — never expose it in client-side code.

Key Format: All keys start with pd_ followed by 32 hex characters. Invalid keys return 401 Unauthorized.
curl -X POST https://pashudhan-nutri-ai.web.app/api/formulate \
  -H "x-api-key: pd_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Quickstart

Get a complete ration in under 60 seconds. Choose your language:

import pashudhan_ai

client = pashudhan_ai.Client(api_key="pd_your_key_here")

result = client.formulate(
    animal_type       = "cow",
    breed             = "HF_crossbred",
    body_weight_kg    = 450,
    milk_yield_kg_day = 18,
    fat_pct           = 3.5,
    production_stage  = "mid_lactation",
    state             = "Punjab",
    month             = 5,
    location          = "30.90,75.85" # Optional: For live THI tracking
)

# Access all 4 optimisation strategies
for opt in result.formulations:
    print(opt.mode, opt.total_cost_local_day)  # e.g. least_cost 248.5

# Or use convenience properties
print(result.least_cost.total_cost_local_day)
for ing in result.least_cost.ingredients:
    print(f"{ing.name}: {ing.kg_fresh_day} kg")

# BioGas Mode example
biogas = client.biogas.formulate(
    animal_type       = "cow",
    breed             = "HF_crossbred",
    body_weight_kg    = 500,
    milk_yield_kg_day = 12,
    fat_pct           = 4.0,
    production_stage  = "mid_lactation",
    state             = "Punjab",
    month             = 6
)
print(biogas.effective_mode)                          # "balanced_biogas" (auto-set)
print(biogas.biogas_analysis.fresh_dung_kg)           # 42.4
print(biogas.biogas_analysis.economics.net_benefit_month)  # 1320.0

AI Agents & MCP Integration

🤖

Model Context Protocol (MCP)

Empower Claude, Cursor, and other AI agents to formulate rations on your behalf.

The Pashudhan Nutri AI platform natively supports the open-source Model Context Protocol (MCP). This means you can instantly give AI assistants the ability to formulate rations, check live heat stress (THI), and query the ICAR-NDRI ingredient database directly within their chat interfaces.

How to connect Claude Desktop

  1. Go to your API Dashboard and generate an Enterprise API Key.
  2. Click the Download MCP Config button next to your key.
  3. This downloads a pashudhan_mcp_config.json file.
  4. Copy the contents of this file into your Claude Desktop configuration file.
    • Windows: %APPDATA%\Claude\claude_desktop_config.json
    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  5. Restart Claude Desktop. The AI will now have access to the Pashudhan Formulation Tools!
Security Note: The MCP Server runs 100% locally on your machine and uses your existing Enterprise API key as a secure bridge. The AI model itself never sees your raw API key.

Python SDK — Install

📦 Official SDK: The pashudhan_ai package wraps the REST API with a clean, Pythonic interface. No need to handle HTTP headers or JSON manually.

Install via pip

pip install pashudhan_ai

Requirements

Python >= 3.8
requests >= 2.28

Python SDK — Usage

Initialize the client

import pashudhan_ai

# Option 1: Pass key directly
client = pashudhan_ai.Client(api_key="pd_your_key_here")

# Option 2: Set environment variable PASHUDHAN_API_KEY
client = pashudhan_ai.Client()  # reads from env automatically

Formulate a ration

result = client.formulate(
    animal_type       = "cow",
    breed             = "Sahiwal",
    body_weight_kg    = 380,
    milk_yield_kg_day = 10,
    fat_pct           = 4.2,
    production_stage  = "mid_lactation",
    state             = "Rajasthan",
    month             = 11,
    use_ai            = True,      # enable AI Nutritionist critique
    location          = "Jaipur"   # fetch live weather data
)

# Access all 4 strategies
for opt in result.formulations:
    print(f"[{opt.mode}] Cost: {opt.total_cost_local_day}/day  NEL={opt.nutrients.nel_mcal_kg_dm} Mcal/kg")

# Access AI Explanation
if result.ai_explanation:
    print("AI says:", result.ai_explanation)

# Access Live THI data
if result.thi_report:
    print(f"Live THI: {result.thi_report.thi} ({result.thi_report.category})")

# Access Metabolic Report (Requires Pro/Enterprise Plan)
if result.metabolic_report:
    print(f"Methane: {result.metabolic_report.methane.enteric_ch4_g_day} g/day")
    print(f"RUP/RDP Status: {result.metabolic_report.rup_rdp.ratio_status}")

Check your usage

status = client.status()
print(status.plan)          # "starter" / "pro" / "pro_max"
print(status.calls_used)    # API calls used this month
print(status.calls_limit)   # monthly limit

API Reference

POST /api/formulate

The core endpoint. Returns up to 4 optimized ration strategies in JSON.

Request Body

ParameterTypeDescription
animal_type requiredstringcow, buffalo, heifer, bull
breed requiredstringe.g. HF_crossbred, Sahiwal, Murrah
body_weight_kg requirednumberBody weight in kg (e.g. 450)
milk_yield_kg_day requirednumberDaily milk yield in kg
fat_pct requirednumberMilk fat % (e.g. 3.5). Only used if lactating.
protein_pct optionalnumberMilk protein % (e.g. 3.2). Default: breed avg. Only used if lactating.
production_stage requiredstringlactating, dry, growing, pre_calving
dim optionalnumberDays in Milk. Affects intake & ketosis risk scoring.
parity optionalnumberLactation number. Default 1. Used for lactating/dry.
days_pregnant optionalnumberDays of pregnancy. Triggers fetal energy demands if > 60.
bcs optionalnumberBody Condition Score (1-5). Default 3.0.
health_flags optionalarrayStrings e.g. ["ketosis_history", "steaming_up"]
state requiredstringState or region (e.g. Punjab, Gujarat)
month requirednumberMonth 1–12 for seasonal ingredient filter
use_ai optionalbooleanEnable AI Nutritionist critique (Pro/ProMax only)
location optionalstringCity or lat,lon for live THI heat stress adjustment
farm_mode optionalstringOptimization goal: "dairy" (default), "biogas", or "balanced_biogas". When omitted, uses the account's saved farm mode from profile. Lactating animals in "biogas" mode auto-switch to "balanced_biogas".
milk_price_inr optionalnumberMilk price per liter in INR (India) or USD (USA). Overrides the saved profile price for this request only. Used in economic analysis. Defaults to state-wise average.

Response

{
  "request_id": "req_abc123",
  "formulations": [
    {
      "mode": "least_cost",
      "total_cost_local_day": 248.50,
      "feed_cost_local_day": 220.0,
      "formulation": {
        "ingredients": [
          { "name": "Wheat Straw",  "kg_fresh_day": 5.0, "cost_local_day": 22.5 },
          { "name": "Maize Grain",  "kg_fresh_day": 3.5, "cost_local_day": 87.5 }
        ],
        "nutrient_summary": { "NEL_Mcal_day": 22.1, "CP_g_day": 1680, "NDF_pct_DM": 32.4 },
        "ai_explanation": "Option 1 is the cheapest but has severe nutrient gaps. A calcium supplement MUST be added.",
        "ai_warnings": [
          "DANGEROUS Ca:P RATIO: The calcium-to-phosphorus ratio is 0.51 — far below the safe minimum of 1.5:1."
        ]
      },
      "palatability": { "score": 88, "refusal_probability_pct": 8 },
      "adaptive_reasoning": ["Ration is nutritionally balanced. Consider adding bypass fat..."],
      "biogas_analysis": {
        "effective_mode": "balanced_biogas",
        "mode_override_reason": "Lactating animal \u2014 auto-switched to balanced_biogas",
        "fresh_dung_kg": 42.4,
        "fecal_dm_kg": 7.2,
        "biogas_liters": 1350,
        "methane_liters": 810,
        "slurry_kg": 22.0,
        "economics": {
          "biogas_revenue_day": 47.0,
          "milk_revenue_change_day": -14.0,
          "slurry_revenue_day": 11.0,
          "net_benefit_day": 44.0,
          "net_benefit_month": 1320.0
        }
      }
    }
  ],
  "thi_report": { "thi": 78, "category": "mild_stress", "adjusted_water_L_day": 82 },
  "metabolic_report": { 
    "methane": { "enteric_CH4_g_day": 345, "CO2e_kg_year": 3524, "vs_benchmark_pct": -4, "mitigation_tips": ["Increase fat"], "applicable": true },
    "rup_rdp": { "ratio_status": "Optimal", "actual_rdp_rup_ratio": "65:35", "applicable": true }
  }
}
Feature Availability: The metabolic_report object requires a Pro, ProMax, Enterprise, or EnterpriseMax API plan. Specifically, Methane estimations are only available for functional ruminants (e.g. not pre-ruminant calves) on supported plans. The biogas_analysis object is returned when farm_mode is biogas or balanced_biogas. Enterprise herd-level biogas endpoints require Enterprise or EnterpriseMax plan.
POST /api/herd-formulate

Formulates for an entire herd. Deducts 4 API credits per call.

Request Body

ParameterTypeDescription
groups requiredarrayList of animal group dictionaries (each containing entries)
state requiredstringState or region (e.g. Punjab, Gujarat)
month requirednumberMonth 1–12 for seasonal ingredient filter
farmer_ingredients optionalarrayList of ingredient IDs to restrict formulation to

Example Request Body

{
  "state": "Punjab",
  "month": 5,
  "farmer_ingredients": ["Wheat Straw", "Maize Grain"],
  "groups": [
    {
      "category": "Lactating",
      "entries": [
        {
          "animal_type": "cow",
          "breed": "HF_crossbred",
          "count": 10,
          "body_weight_kg": 500,
          "milk_yield_kg_day": 20,
          "milk_fat_pct": 3.5,
          "production_stage": "mid_lactation"
        }
      ]
    }
  ]
}

Example Response

{
  "herd_summary": {
    "total_animals": 10,
    "total_feed_cost_inr": 2485.00,
    "avg_cost_per_animal_inr": 248.50,
    "thi": 78
  },
  "groups": [
    {
      "category": "Lactating",
      "animal_count": 10,
      "total_group_cost_inr": 2485.00,
      "formulation": {
        "strategy": "least_cost",
        "ingredients": [
          { "name": "Wheat Straw", "kg_fresh_day": 50.0, "cost_inr": 225.0 },
          { "name": "Maize Grain", "kg_fresh_day": 35.0, "cost_inr": 875.0 }
        ],
        "nutrients": { "NEL_Mcal_day": 221.0, "CP_g_day": 16800 },
        "ai_explanation": "Group is well-balanced. Monitor heat stress impact on DMI."
      }
    }
  ]
}
GET /api/plans

Returns all available subscription plans. No authentication required.

curl https://pashudhan-nutri-ai.web.app/api/plans
GET /api/enterprise/status

Returns your current plan and monthly API usage. Requires x-api-key header or Authorization: Bearer <firebase-token>.

curl https://pashudhan-nutri-ai.web.app/api/enterprise/status \
  -H "x-api-key: pd_your_key_here"

# Response:
{
  "plan": "starter",
  "monthly_limit": 1000,
  "requests_used": 142
}
GET /api/ingredients-list

Returns all available ingredients with their composition for a given country. This allows you to build custom ingredient picker UIs. Requires x-api-key. By default, it returns Indian ingredients with INR prices. Passing ?country=USA will return USA ingredients with USD prices.

# India (Default)
curl "https://pashudhan-nutri-ai.web.app/api/ingredients-list?country=India" \
  -H "x-api-key: pd_your_key_here"

# USA
curl "https://pashudhan-nutri-ai.web.app/api/ingredients-list?country=USA" \
  -H "x-api-key: pd_your_key_here"

♻️ BioGas Formulation

Dedicated endpoints for biogas farms to maximize dung and excreta output for biogas digesters, while keeping animals within safe physiological and nutritional limits. Supports India (INR) and USA (USD) ingredient databases.

POST /api/biogas/formulate

Generate a biogas-optimized ration for a single animal. Deducts 2 API credits. Uses the same animal parameters as /api/formulate, plus the optional biogas_mode parameter.

ParameterTypeDescription
biogas_mode optionalstring"biogas_dung_max" (default) or "balanced_biogas". The system auto-overrides to balanced_biogas for lactating / late-pregnant animals.
biogas_price_per_m3 optionalnumberCBG or biogas sale price in ₹/m³. Defaults to saved enterprise setting or ₹35/m³.
milk_price_inr optionalnumberMilk price per liter. Defaults to saved profile setting or state average.
All other parameters same as /api/formulate (animal_type, body_weight_kg, state, month, etc.)
POST /api/biogas/compare

Side-by-side comparison: runs both dairy mode and biogas mode for the same animal and returns both results plus a comparison block. Deducts 3 API credits.

POST /api/enterprise/biogas/herd-analysis

Enterprise only. Compute total herd-level dung output, daily biogas potential, recommended digester size (m³), and herd-level economics from a list of animal groups. Deducts 6 API credits. Maximum 500 animals per call.

GET /api/enterprise/biogas/settings
PATCH /api/enterprise/biogas/settings

Read or update enterprise-level biogas configuration: plant capacity (m³), CBG price (₹/m³), slurry price (₹/kg), and default farm mode.

Feature Availability: The /api/biogas/* endpoints are available on all plans. The /api/enterprise/biogas/* endpoints require an Enterprise or EnterpriseMax API plan. Enterprise accounts get higher rate limits: 100 req/min vs 30 req/min for standard accounts.

⚡ Cooperative Biogas API

Configure biogas mode for your cooperative and run herd-level analysis via API.

GET /api/enterprise/biogas/settings

Get current biogas configuration.

PATCH /api/enterprise/biogas/settings
{"mode": "dual", "reactor_volume_m3": 20, "target_gas_m3_day": 10, "gas_price_inr_m3": 35, "notes": "Collect slurry at 6am daily"}

mode: none | biogas | dual

POST /api/enterprise/biogas/herd-analysis

Request

{"herd_size": 50, "avg_body_weight_kg": 400}

Response

{"daily_gas_m3": 45.2, "monthly_revenue_inr": 47460, "slurry_kg_day": 900, "digestate_value_inr": 12000}

SDK Example

# Configure biogas mode
coop.update_biogas_settings(mode='dual', gas_price_inr_m3=35, notes='Collect at 6am')

# Run herd analysis
result = coop.run_herd_biogas_analysis(herd_size=50, avg_body_weight_kg=400)
print(f"Daily gas: {result['daily_gas_m3']} m3/day")
print(f"Monthly revenue: Rs {result['monthly_revenue_inr']}")
GET /api/countries

Returns a list of supported countries and their configurations (currency, regions). No authentication required.

curl https://pashudhan-nutri-ai.web.app/api/countries
GET /api/breeds

Returns the dairy breeds available for a specific country, along with their default body weights and milk fat metrics. No authentication required.

# India (Default)
curl "https://pashudhan-nutri-ai.web.app/api/breeds?country=India"

# USA
curl "https://pashudhan-nutri-ai.web.app/api/breeds?country=USA"

Custom Ingredients Management

Add your cooperative's own proprietary feed products — commercial concentrates, in-house mineral mixes, or lab-analyzed local ingredients — to the formulation engine. These are stored in a completely separate database from individual B2C users and from other cooperatives.

🔑 B2B vs B2C Isolation:
• B2C individual custom ingredients → users/{uid}/custom_ingredients/
• B2B cooperative custom ingredients → enterprises/{coop_id}/custom_ingredients/

⚡ Auto-inject: Every formulation request made with your pd_ API key automatically loads your cooperative's custom ingredients into the LP solver — no extra parameter needed. Farmers calling your branded API get formulations that include your products by default.

SDK Example — Add a Cooperative Feed Product

import pashudhan_ai

client = pashudhan_ai.Client(api_key="pd_your_enterprise_key")

# Add your cooperative's formulated concentrate
# Values come from the feed bag or your lab analysis
ing = client.add_custom_ingredient(
    name             = "Amul Krishi Concentrate A",
    CP_pct           = 22.0,   # Crude Protein % (bag label)
    fat_pct          = 3.5,    # Crude Fat %
    fibre_pct        = 8.0,    # Crude Fibre %
    ash_pct          = 9.0,    # Total Ash %
    moisture_pct     = 11.0,   # Moisture %
    price_inr_per_kg = 26.0,   # Your cost price ₹/kg
    category         = "energy_concentrate",
    Ca_pct           = 0.8,    # Optional: Calcium if on bag
    P_pct            = 0.6,    # Optional: Phosphorus if on bag
    max_inclusion_pct= 35.0,   # Max % of DM ration
)
print(ing["id"])        # custom_abc123
print(ing["TDN_pct"])   # 82.24  ← auto-derived by server
print(ing["NEL_Mcal_kg_DM"])  # 1.74  ← auto-derived

# ── Now every formulation via your API key includes this ingredient ──
result = client.formulate(
    animal_type="cow", breed="HF_crossbred",
    body_weight_kg=500, milk_yield_kg_day=20.0, fat_pct=3.8,
    production_stage="mid_lactation",
    state="Gujarat", month=6,
)
# Amul Krishi Concentrate A appears in result if nutritionally beneficial
POST /api/enterprise/ingredients

Create a new custom ingredient. The server will automatically compute all 15+ derived nutrient values (TDN, NDF, energy fractions) based on the 5 mandatory inputs.

Request Body

ParameterTypeDescription
name requiredstringName of your custom feed (e.g. My Special Pellets)
price_INR_kg_fresh requirednumberCost per kg fresh (e.g. 24.50)
moisture_pct requirednumberMoisture percentage (e.g. 10.0)
CP_pct_DM requirednumberCrude Protein % on Dry Matter basis
EE_pct_DM requirednumberEther Extract (Fat) % on Dry Matter basis
crude_fibre_pct requirednumberCrude Fibre % on Dry Matter basis
ash_pct_DM requirednumberTotal Ash (Minerals) % on Dry Matter basis
{
  "name": "Super Yield Pellet",
  "price_INR_kg_fresh": 22.50,
  "moisture_pct": 12.0,
  "CP_pct_DM": 20.0,
  "EE_pct_DM": 4.5,
  "crude_fibre_pct": 10.0,
  "ash_pct_DM": 8.0
}
GET /api/enterprise/ingredients

List all custom ingredients associated with your enterprise key.

PUT /api/enterprise/ingredients/{ing_id}

Update an existing custom ingredient. The server will recalculate derived values if you change the primary macro-nutrients.

DELETE /api/enterprise/ingredients/{ing_id}

Permanently delete a custom ingredient.

🌿 Feeding Best Practices

The Pashudhan Nutri AI API returns scientifically optimised rations. To help your users get the best real-world outcomes, consider surfacing the following guidance alongside the formulation results.

Total Mixed Ration (TMR) — Prevent Sorting

When feeding a TMR, mix dry roughage with concentrates and wet ingredients (silage, green fodder) thoroughly in the manger or a chaff cutter. Add a small amount of water or molasses to bind fine particles. This prevents dominant animals from sorting out concentrates and consuming excess starch, which causes acidosis and laminitis in high-yielding cows.

💡 API Tip: If the vet_rules response contains a sorting or dominance warning, display a prominent banner to your user advising them to use TMR mixing.

Transition Cow Management (21 Days Before Calving)

The transition period is the most critical phase in a dairy cow's production cycle. Follow these rules for close-up dry cows:

  • Feed anionic salts (e.g., calcium chloride, ammonium sulphate) to keep DCAD negative (-10 to -15 mEq/100g DM), which prevents milk fever (hypocalcaemia).
  • Limit dietary calcium to < 0.5% DM to prime the parathyroid hormone response before calving.
  • Avoid over-conditioning — target BCS 3.0–3.25 at calving to reduce risk of fatty liver and ketosis.
  • Provide propylene glycol (300 mL/day) as a gluconeogenic precursor for cows with BCS > 3.5 going into the dry period.
💡 API Tip: The API uses production_stage: "close_up_dry" to trigger DCAD-targeted formulation automatically. Always pass this stage for animals within 3 weeks of expected calving.

Heat Stress Mitigation (THI > 72)

When the Temperature-Humidity Index (THI) exceeds 72, dairy cows can lose 2–4 kg of milk per day. The API's THI response field signals when heat stress is active. Advise your users to:

  • Shift feeding times to early morning (5–7 AM) and late evening (7–10 PM) when ambient temperature is lower and DMI is higher.
  • Increase bypass fat (e.g., Calcium Soap of Fatty Acids, 200–300 g/day) to compensate for reduced DMI without increasing rumen heat load.
  • Ensure continuous access to clean, cool water — heat-stressed cows drink 50% more. Install shade nets over water troughs.
  • Add sodium bicarbonate (100–150 g/day) as a rumen buffer — heat stress reduces salivation and ruminal buffering capacity.
💡 API Tip: Pass the location field (city or "lat,lon") in individual formulation requests. The API automatically fetches live THI and adjusts NDF and energy constraints.

🐄 Herd Management Tips

Grouping Strategy

For herd formulation to be most effective, group animals with similar nutritional needs. The following grouping strategy is recommended for semi-intensive farms:

GroupAnimalsKey Nutritional Priority
High-yield LactatingFresh cows + cows giving >20 L/day (DIM 5–100)Max NEL density, bypass protein
Mid-yield LactatingCows giving 10–20 L/day (DIM 100–200)Balanced cost/performance
Late Lactation / Dry OffCows giving <10 L/day (DIM >200)Reduce energy, prevent over-conditioning
Close-Up Dry21 days before calvingAnionic diet, low Ca, monitor BCS
Far-Off Dry60–22 days before calvingMaintenance only, high roughage
Heifers0–24 monthsGrowth, mineral balance, no urea

Manger Space & Feeding Frequency

  • Provide minimum 60 cm of manger space per cow. Inadequate space is the single biggest cause of low DMI and feed sorting in most farm conditions.
  • Feed at least twice daily (morning + evening). For high-yielding cows (>25 L), three feedings significantly improve DMI and milk fat.
  • Push up feed every 2–3 hours to encourage eating. Cows eat most actively in the 2 hours after milking.
  • Weigh feed refusals daily. Target 3–5% refusal — if refusals are zero, the cow is underfed; if >10%, the ration palatability is low.

Monitoring & Body Condition Score (BCS)

BCS is the most practical on-farm indicator of energy balance. Train your users to score their herd monthly:

StageTarget BCSAction if BelowAction if Above
Calving3.0 – 3.25Increase concentrate pre-calvingReduce energy in dry period
Early Lactation (DIM 0–60)2.5 – 3.0Add bypass fat, limit milk goalMonitor for fatty liver
Peak / Mid Lactation2.75 – 3.25Increase energy densityNormal range
Drying Off3.0 – 3.5Supplement energy, check parasitesRestrict concentrate
📢 SDK Tip: Pass avg_bcs in each herd entry. The API uses this to detect negative energy balance risk and may return a NEB warning in warnings[].

🌞 Seasonal Feeding Calendar

Ingredient availability and nutritional value varies significantly by season in India. Use the month field in all API calls — the engine automatically adjusts ingredient prices and availability based on the season for the given state.

SeasonMonthsKey Available FeedsWatch Out For
Summer (Garmi)March – JuneMaize silage (if stored), dry straw, cotton seedHeat stress (THI > 72), water scarcity, low green fodder — supplement bypass fat
Monsoon (Barsaat)July – SeptemberFresh green fodder (napier, bajra, maize), cheap pricesMycotoxin contamination in wet silage, low DM content in fresh fodder
Post-Monsoon (Kharif Harvest)October – NovemberMaize grain, groundnut cake, soyabean meal — peak availability and lowest pricesAflatoxin in groundnut products post-rains — insist on tested batches
Winter (Sardi)December – FebruaryWheat bran, mustard cake, berseem/lucerne (premium green fodder)Optimal DMI season — highest milk yields expected; maximise production
Spring (Rabi Harvest)MarchWheat straw (new crop), cheap branTransition from good winter to summer heat — start heat stress protocols early

🧬 Biological Constraints & Validations

The Pashudhan Nutri API actively guards against biologically impossible inputs to ensure generated formulations remain safe and adhere to ICAR & NASEM standards. All limits are widened appropriately to accommodate the longer gestation and slower maturity of Buffalo breeds.

Strict Validations: If you submit values outside these ranges, the API will reject the request with an HTTP 400 Bad Request and a detailed ValidationError explaining the biological mismatch.
ParameterConstraintDescription
body_weight_kg10 to 1500Captures from small newborn calves up to mature heavy bulls.
milk_yield_kg_day≤ 100Maximum plausible physiological limit for daily yield.
fat_pct0.5 to 15.0Ensures valid energy correction mathematics.
protein_pct1.5 to 7.0Ensures valid milk protein limits.
days_pregnant≤ 320Accommodates standard cow gestation (~285) and buffalo (~315).
dim (Days in Milk)≤ 1000Prevents infinite lactation curves.
parity1 to 20Biological limits on the number of lactations.
bcs1.0 to 5.0Body condition scoring limits.
age_months≤ 360 (30 years)Absolute biological maximum age.
Stage MismatchCalf max 18 mo.
Heifer max 48 mo.
The API will aggressively reject a 48-month-old calf, enforcing standard maturity timelines.
Health Flags LogicConditionalprotein_pct only used if lactating. steaming_up only used if pregnant/dry.

⚠ Error Codes Reference

The API returns standard HTTP status codes. Always check the detail field in error responses for a human-readable message.

CodeMeaningResolution
200SuccessProcess the response normally.
400Bad Request — invalid payloadCheck required fields, animal count limits (<=15 for herd), valid month (1–12).
401Unauthorised — missing or invalid API keyVerify the x-api-key header is included and the key is active.
403Forbidden — plan limit exceeded or wrong roleCheck your credit balance via GET /api/enterprise/status. Upgrade plan if needed.
429Rate Limited — monthly quota exhaustedMonthly limit reset on the 1st. Upgrade to Pro API or Enterprise Max plan.
500Server Error — formulation engine issueRetry once. If persistent, contact support with the request body and timestamp.
💡 SDK Behaviour: The Python SDK maps these to typed exceptions — AuthError (401/403), ValidationError (400), RateLimitError (429), and APIError (5xx) — so you can handle them cleanly in application code.

🐄 Animal Registry API

The Animal Registry API lets your platform register named animals and build personalised nutrition intelligence per animal. Each animal accumulates real milk-slip outcomes that auto-update a learned_profile — running averages for fat%, SNF%, and milk yield. This profile is automatically used by /api/formulate when animal_id is supplied.

Supported Plans: All authenticated accounts (individual & enterprise). Enterprise accounts can additionally namespace animals to a specific dairy_id.

POST /api/animals/register — Register Animal

POST/api/animals/register
FieldTypeRequiredDescription
namestringREQAnimal name or label (e.g. "Rani").
animal_typestringREQ"cow" | "buffalo" | "heifer" | "bull" | "calf_weaned"
breedstringREQBreed string (e.g. "Murrah_buffalo"). See GET /api/breeds.
current_stagestringOPTProduction stage. Default: "mid_lactation".
parityintOPTNumber of calves born so far. Default: 1.
tag_idstringOPTEar tag / farm ID number.
dairy_idstringOPTFarm/dairy ID for enterprise multi-farm namespacing.
import pashudhan_ai

client = pashudhan_ai.Client(api_key="pd_your_key_here")

# Register an animal
animal = client.register_animal(
    name             = "Rani",
    animal_type      = "buffalo",
    breed            = "Murrah_buffalo",
    current_stage    = "mid_lactation",
    parity           = 3,
    tag_id           = "MH-042",
)
print(animal.animal_id)   # e.g. "a3f9c1b2"

GET /api/animals — List All Animals

GET/api/animals

Returns all registered animals for the authenticated account, newest first. Each animal includes its learned_profile with running averages. Filter by dairy using the optional ?dairy_id= query parameter.

# List all animals
animals = client.list_animals()
for a in animals:
    lp = a.learned_profile
    print(a.name, f"avg_fat={lp.avg_fat_pct}% conf={lp.confidence_level}")

# Filter by dairy farm
farm_animals = client.list_animals(dairy_id="d4a2b7c8e1")

POST /api/animals/{id}/outcomes — Submit Outcome

POST/api/animals/{animal_id}/outcomes

Submit a real-world milk-slip result for an animal. Each submission automatically recalculates the animal's learned_profile (running averages for fat%, SNF%, milk yield) and raises the confidence level as more data accumulates.

FieldTypeRequiredDescription
actual_milk_yield_kgfloatREQActual milk yield (kg/day) from today's slip.
actual_fat_pctfloatREQActual milk fat %.
actual_snf_pctfloatOPTSNF % if available.
days_elapsedintOPTDays since the ration was applied. Default: 7.
health_eventslistOPTObservations e.g. ["normal"].
bcs_scorefloatOPTBody condition score (1–5).
ration_idstringOPTReport ID of the ration that was fed.
# Submit a milk-slip outcome
outcome = client.record_outcome(
    animal_id            = "a3f9c1b2",
    actual_milk_yield_kg = 19.5,
    actual_fat_pct       = 7.2,
    actual_snf_pct       = 8.9,
    health_events        = ["normal"],
)

# After 15+ outcomes, confidence = "high" — formulations
# automatically use the animal's learned profile
updated = client.get_animal("a3f9c1b2")
print(updated.learned_profile.confidence_level)   # "medium" / "high"
print(updated.learned_profile.avg_fat_pct)         # e.g. 7.1

Full Animal SDK Reference

# All animal registry methods
client.register_animal(name, animal_type, breed, ...)  # → AnimalProfile
client.list_animals(dairy_id=None)                     # → List[AnimalProfile]
client.get_animal(animal_id)                           # → AnimalProfile
client.delete_animal(animal_id)                        # → bool
client.record_outcome(animal_id, milk_kg, fat_pct, ...) # → AnimalOutcome

# AnimalProfile attributes
animal.animal_id          # "a3f9c1b2"
animal.name               # "Rani"
animal.breed              # "Murrah_buffalo"
animal.fat_trend          # "above_avg" / "average" / "below_avg" (property)
animal.learned_profile    # LearnedProfile object
  .avg_fat_pct            # 7.1
  .avg_milk_yield_kg      # 19.2
  .confidence_level       # "low" | "medium" | "high"
  .total_outcomes_recorded  # 18

🏭 Multi-Dairy API

Enterprise accounts can manage multiple dairy farms under a single API key. Each dairy has its own animals, custom ingredients, and performance history. Use dairy_id to namespace animals and formulation requests to a specific farm.

Enterprise Only. Multi-dairy management requires an ent_pro or ent_promax plan. Individual accounts see a single default farm.

POST /api/dairies — Register a Dairy Farm

POST/api/dairies
FieldTypeRequiredDescription
namestringREQFarm name (e.g. "Singh Dairy — Ludhiana Unit 2").
locationstringOPTFarm city/state (e.g. "Ludhiana, Punjab").
# Create a dairy farm
farm = client.register_dairy(
    name     = "Singh Dairy — Ludhiana Unit 2",
    location = "Ludhiana, Punjab",
)
print(farm.dairy_id)   # e.g. "d4a2b7c8e1"

# Register animals under this farm
animal = client.register_animal(
    name        = "Rani",
    animal_type = "buffalo",
    breed       = "Murrah_buffalo",
    dairy_id    = farm.dairy_id,   # ← namespace to farm
)

GET /api/dairies — List All Farms

GET/api/dairies

Returns all dairy farms registered under this enterprise account. Each farm includes its animal_count.

# List all dairy farms
farms = client.list_dairies()
for farm in farms:
    print(farm.name, farm.location, farm.animal_count)

# Full per-farm workflow
farms = client.list_dairies()
for farm in farms:
    animals = client.list_animals(dairy_id=farm.dairy_id)
    print(f"{farm.name}: {len(animals)} animals")
    for a in animals:
        result = client.formulate(
            animal_type       = a.animal_type,
            breed             = a.breed,
            body_weight_kg    = 480,
            milk_yield_kg_day = a.learned_profile.avg_milk_yield_kg or 15,
            fat_pct           = a.learned_profile.avg_fat_pct or 6.5,
            production_stage  = a.current_stage,
            state             = "Punjab",
            month             = 7,
        )
        print(f"{a.name}: ₹{result.least_cost.total_cost_local_day:.0f}/day")

Full Multi-Dairy SDK Reference

# Multi-dairy methods (enterprise only)
client.register_dairy(name, location="")   # → DairyFarm
client.list_dairies()                       # → List[DairyFarm]
client.delete_dairy(dairy_id)               # → bool (deletes all animals)

# DairyFarm attributes
farm.dairy_id      # "d4a2b7c8e1"
farm.name          # "Singh Dairy — Ludhiana Unit 2"
farm.location      # "Ludhiana, Punjab"
farm.animal_count  # 24
farm.created_at    # ISO timestamp
💡 SDK v0.4.0 — Install the latest: pip install --upgrade pashudhan_ai

🚀 Cooperative Setup Guide — API Only

The web dashboard at /coop-admin is optional. Every cooperative feature is fully accessible via the REST API and the Python SDK. This guide walks a cooperative through complete setup without opening a browser once.

Prerequisites:
1. An Enterprise account — generate an API key (plan: ent_pro or ent_promax).
2. A Meta Business WhatsApp number (optional — only if you want WhatsApp bot).
3. Python 3.8+ and pip install pashudhan_ai.

Step 1 — Get Your Cooperative ID

Your coop_id is your Firebase UID, embedded in your API key's metadata. It's used as the WhatsApp webhook path segment.

import pashudhan_ai
client = pashudhan_ai.Client(api_key="pd_your_enterprise_key")

# Get your coop_id and current plan status
status = client.status()
print(status.plan)         # ent_pro
print(status.calls_used)   # 247

billing = client.cooperative.get_billing()
print(billing)
# {"plan": "ent_pro", "monthly_limit": 1000, "requests_used": 247, ...}

# Your coop_id is visible in the billing keys list
keys = client.list_api_keys()
print(keys[0]["key_prefix"])   # pd_a1b2c3... (first 10 chars)

Step 2 — Custom Branding

Applied instantly to tenant-loader.js injections and the WhatsApp bot greeting.

client.cooperative.set_branding(
    company_name    = "Amul Dairy Tech",
    tagline         = "Smart Nutrition for Every Farmer",
    logo_url        = "https://cdn.amul.com/logo.png",
    primary_color   = "#B71C1C",
    secondary_color = "#F57F17",
    contact_email   = "support@amuldairy.com",
    contact_phone   = "+91-2692-258506",
)

Step 3 — Connect Your WhatsApp Number

Farmers message your number and receive ration advice in your brand. Pashudhan's engine runs silently behind the scenes.

# 1. Save credentials (your Meta Cloud API token + Phone Number ID)
client.cooperative.set_whatsapp(
    phone_number_id = "123456789012345",
    access_token    = "EAABwzLixnjY...",
    verify_token    = "my_coop_verify_secret",  # you choose this
    display_name    = "Amul Krishi Sahayak",
)

# 2. In Meta Developer Console → Webhooks, register:
#    Webhook URL:  https://pashudhan-nutri-ai.web.app/api/whatsapp/coop/{coop_id}
#    Verify Token: my_coop_verify_secret
#    Fields:       messages
#
# That's it. Farmers now WhatsApp your number and get rations in your brand.
How it works: Pashudhan receives the message on your behalf via POST /api/whatsapp/coop/{coop_id}, runs the full ICAR/NASEM formulation engine, applies your recommendation rules, then replies to the farmer using your Meta access_token and phone_number_id. Your WhatsApp credentials are stored encrypted in Firestore under users/{coop_id}/config/whatsapp and never logged or returned in full.

Step 4 — Private Feed Ingredient Database

Add your cooperative's own commercial feeds. These auto-inject into every formulation run with your API key — no extra parameter needed per call.

# Add your concentrate (values from feed bag or lab analysis)
client.add_custom_ingredient(
    name             = "Amul Krishi Concentrate A",
    CP_pct           = 22.0,
    fat_pct          = 3.5,
    fibre_pct        = 8.0,
    ash_pct          = 9.0,
    moisture_pct     = 11.0,
    price_inr_per_kg = 26.0,
    category         = "energy_concentrate",
)
# Server auto-derives TDN, NEL, NDF, ADF, ME, DE from these 5 inputs
# Stored at: enterprises/{coop_id}/custom_ingredients/
# Loaded automatically for every /api/formulate call with your API key

Step 5 — Register Your Product Catalog

Rules fire after every formulation. When a nutrient gap is detected, your product recommendation appears in the API response and in the WhatsApp reply to the farmer.

coop = client.cooperative

# 5a. Add product to catalog
prod = coop.add_product(
    name           = "MilkBoost Mineral Mix",
    sku            = "AMBMM-500",
    category       = "mineral_supplement",
    target_nutrient= "Ca_pct_DM",
    dose_g_per_day = 100,
    price_inr      = 85,
    product_link   = "https://shop.amul.com/milkboost",
)

# 5b. Create recommendation rule: Ca deficit > 10% → recommend MilkBoost
coop.add_rule(
    nutrient_key       = "Ca_pct_DM",
    trigger_condition  = "deficit_pct_above",
    threshold          = 10,
    recommendation_type= "product",
    product_id         = prod["id"],
    suggested_qty_g    = 100,
    message_template   = "Ca deficit {gap_pct}%! Add {qty}g of {source} daily to fix this.",
    priority           = 3,
)

# 5c. Absolute threshold: Zn below 40 mg/kg → recommend zinc sulfate
coop.add_rule(
    nutrient_key       = "Zn_mg_kg",
    trigger_condition  = "below_absolute",
    threshold          = 40,
    recommendation_type= "ingredient",
    ingredient_id      = "zinc_sulfate",
    ingredient_name    = "Zinc Sulfate",
    suggested_qty_g    = 5,
    priority           = 1,
)

Step 6 — Webhooks for Your ERP/CRM

# All events (ERP integration)
coop.add_webhook(
    url="https://api.amuldairy.com/events",
    events=["*"],
    description="Main ERP — all events",
)

# Deficiency alerts only (SMS gateway / field officer app)
coop.add_webhook(
    url="https://sms.amul.com/deficiency-alerts",
    events=["deficiency.detected"],
    description="SMS alerts to field officers",
)

# Test delivery to verify your endpoints are reachable
for wh in coop.list_webhooks():
    result = coop.test_webhook(wh["id"])
    print(f"{wh['url']}: {'✓' if result['success'] else '✗'} {result['status_code']} ({result['duration_ms']}ms)")

Step 7 — Run Formulations

With setup complete, any formulation via your API key automatically:

  • Loads your cooperative's private ingredient catalog into the solver
  • Evaluates your recommendation rules against nutrient gaps
  • Fires webhooks for your ERP/CRM
  • Returns cooperative_recommendations[] in the response
  • Credits usage against your monthly API limit
result = client.formulate(
    animal_type       = "cow",
    breed             = "Sahiwal",
    body_weight_kg    = 420,
    milk_yield_kg_day = 14.0,
    fat_pct           = 4.5,
    production_stage  = "mid_lactation",
    state             = "Gujarat",
    month             = 6,
)

# Standard formulation output
least_cost = result.formulations[0]
print(f"Cost: ₹{least_cost.total_cost_inr}/day")

# B2B-only: your rules' recommendations injected into response
recs = result.cooperative_recommendations if hasattr(result, 'cooperative_recommendations') else []
for rec in recs:
    print(f"  [{rec['nutrient']}] {rec['message']}")
    print(f"  → Buy: {rec['link']}")

Step 8 — Monitor

# Dashboard stats
dash = coop.get_dashboard()
print(f"Farmers served: {dash['total_farmers']}")
print(f"API calls:      {dash['api_calls_used']}/{dash['api_calls_limit']}")

# Farmer analytics (build your own CRM)
farmers = coop.list_farmers()
from collections import Counter
breeds = Counter(f["breed"] for f in farmers)
print(dict(breeds.most_common(5)))

# Webhook delivery health
for wh in coop.list_webhooks():
    logs = coop.get_webhook_logs(wh["id"])
    failed = [l for l in logs if not l["success"]]
    print(f"{wh['url']}: {len(failed)}/{len(logs)} failed")

What Each Feature Touches (Data Isolation)

FeatureFirestore PathIsolated?
Brandingusers/{coop_id}/config/branding✅ Per-coop
WhatsApp credentialsusers/{coop_id}/config/whatsapp✅ Per-coop, token never returned in full
Custom feed ingredientsenterprises/{coop_id}/custom_ingredients/✅ Separate from B2C users
Product catalogusers/{coop_id}/products/✅ Per-coop
Recommendation rulesusers/{coop_id}/rules/✅ Per-coop
Webhooks + logsusers/{coop_id}/webhooks/{id}/entries/✅ Per-coop
Farmer analyticsusers/{coop_id}/farmers/✅ Per-coop
API keysapi_keys/{key_id} with cooperative_id field✅ Keyed to coop only

🏮 Cooperative Platform API

The Cooperative Platform is a branded B2B layer on top of the core formulation engine. It lets dairy cooperatives, agri-tech companies, and feed manufacturers embed Pashudhan Nutri AI under their own brand — with their own WhatsApp number, recommendation rules, product catalog, and webhook integrations.

Enterprise Plans Only. All /api/coop-admin/* endpoints require an x-api-key: pd_xxx Enterprise API key (ent_pro or ent_promax). The ent_free plan has read-only access.

How it works

  1. Generate an Enterprise API key on the API Dashboard.
  2. Open the Coop Admin Console to configure branding, webhooks, rules, and products.
  3. Add <meta name="pd-api-key" content="pd_xxx"> and <script src="/tenant-loader.js" defer></script> to your branded pages. Your colors and logo inject automatically.
  4. Point your Meta Business WhatsApp number webhook to POST /api/whatsapp/coop/{coop_id}. Your farmers message your WhatsApp number and get formulation results in your brand.
  5. Set up webhooks to receive formulation.complete, deficiency.detected, and recommendation.triggered events in your own database — any tech stack.
GET /api/tenant/config

Returns the cooperative's branding config. Used by tenant-loader.js to inject custom styles into any page.

HeaderRequiredDescription
x-api-keyREQEnterprise API key (pd_xxx)

Response

{
  "cooperative_id": "uid_abc123",
  "plan": "pro",
  "branding": {
    "company_name": "Amul Dairy Tech",
    "tagline":     "Smart Nutrition for Every Farmer",
    "logo_url":    "https://example.com/logo.png",
    "primary_color":   "#B71C1C",
    "secondary_color": "#F57F17",
    "contact_email":   "support@amuldairy.com"
  }
}
GET /api/coop-admin/dashboard

Returns overview statistics for the Coop Admin Console.

Response

{
  "api_calls_used":  847,
  "api_calls_limit": 1000,
  "plan":            "pro",
  "total_farmers":   156,
  "total_rules":     12,
  "total_products":  8
}

👨‍🌾 Farmer Analytics

View all farmer interactions recorded through your cooperative's API key and WhatsApp number. Each interaction is stored with farmer phone (used as anonymous ID), channel, breed, and timestamp. Use this to build your own CRM, SMS followup, or field-agent dashboards — no web dashboard needed.

GET /api/coop-admin/farmers

Response

{
  "farmers": [
    {
      "farmer_id":    "918001234567",
      "channel":      "whatsapp",
      "action":       "formulation",
      "animal_type":  "cow",
      "breed":        "Sahiwal",
      "timestamp":    "2026-09-04T12:00:00Z",
      "cooperative_id": "uid_abc123"
    }
  ],
  "total": 156
}

SDK Example

# Pull all farmer interactions for your CRM
farmers = client.cooperative.list_farmers()

# Count unique farmers
unique = {f["farmer_id"] for f in farmers}
print(f"Unique farmers: {len(unique)}")

# Group by breed for analytics
from collections import Counter
breed_counts = Counter(f["breed"] for f in farmers)
for breed, count in breed_counts.most_common():
    print(f"  {breed}: {count} formulations")

Webhook Configuration

Register HTTPS endpoints to receive real-time events when formulations run through your cooperative's API key. Each event payload is HMAC-SHA256 signed using your pd_ API key — verify the X-Pashudhan-Signature header on your server.

GET /api/coop-admin/webhooks

List all webhook endpoints.

POST /api/coop-admin/webhooks

Request Body

FieldTypeRequiredDescription
urlstringREQHTTPS endpoint to receive events
eventsarrayREQ["formulation.complete","deficiency.detected","recommendation.triggered","farmer.registered","*"]
descriptionstringOPTHuman-readable label
activeboolOPTDefault: true
POST /api/coop-admin/webhooks/{id}/test

Send a test event to verify your endpoint is reachable. Returns delivery status and response time.

GET /api/coop-admin/webhooks/{id}/logs

Retrieve the last 50 delivery log entries — timestamp, HTTP status, duration, attempt number.

Signature Verification

import hmac, hashlib

# ─────────────────────────────────────────────────────────────────────────
# YOUR pd_ API KEY IS YOUR WEBHOOK SIGNING SECRET
# The same key you use in x-api-key is also used by Pashudhan to sign
# every outgoing webhook payload with HMAC-SHA256.
# X-Pashudhan-Signature contains the raw hexdigest (no "sha256=" prefix).
# ─────────────────────────────────────────────────────────────────────────

def verify_pashudhan_webhook(
    raw_body_bytes: bytes,
    signature_header: str,
    pd_api_key: str
) -> bool:
    """
    Verify that a webhook POST came from Pashudhan and was not tampered with.

    Args:
        raw_body_bytes:    The raw request body as bytes (do NOT re-serialize).
        signature_header:  Value of the X-Pashudhan-Signature header.
        pd_api_key:        Your pd_ Enterprise API key (same as x-api-key).

    Returns:
        True if authentic, False if tampered or wrong key.
    """
    expected = hmac.new(
        pd_api_key.encode("utf-8"),
        raw_body_bytes,
        hashlib.sha256
    ).hexdigest()  # raw hex — no "sha256=" prefix
    return hmac.compare_digest(expected, signature_header)


# ── Flask / FastAPI example ──────────────────────────────────────────────
# from flask import Flask, request, abort
# app = Flask(__name__)
# PD_API_KEY = "pd_your_key_here"
#
# @app.route("/webhook", methods=["POST"])
# def webhook():
#     raw = request.get_data()  # IMPORTANT: raw bytes, before any parsing
#     sig = request.headers.get("X-Pashudhan-Signature", "")
#     if not verify_pashudhan_webhook(raw, sig, PD_API_KEY):
#         abort(401, "Invalid signature")
#     event = request.json
#     print(event["event"], event["data"])  # formulation.complete, {ration:...}
#     return {"ok": True}

Product Catalog

Your cooperative's private product catalog. Register your products and specify which nutrients they address. When the AI formulation engine detects a nutrient deficiency of ≥10% (a system-enforced threshold — not configurable by cooperatives), it automatically finds matching active products from your catalog and recommends them with their purchase link in the API response, PDF, and WhatsApp reply.

How recommendations work: Register a product → set target_nutrients → the AI detects a deficiency → your product is shown with the purchase link. No rules, no thresholds to configure.
GET /api/coop-admin/products
POST /api/coop-admin/products

Fields

FieldTypeDescription
namestringProduct display name
skustringInternal SKU
categorystringe.g. mineral_supplement, energy_supplement
target_nutrientsarrayList of nutrient keys this product addresses, e.g. ["Ca_pct_DM", "P_pct_DM"]. The AI matches these against detected deficiencies. See Nutrient Key Reference.
product_linkstringPurchase/promo URL shown to the farmer when this product is recommended. Use your shop, Amazon, or affiliate link.
descriptionstringFree text description
activeboolWhether to include in recommendations
image_urlstringProduct thumbnail URL (from Firebase Storage)
DELETE /api/coop-admin/products/{product_id}

🌿 Enterprise Ingredients

Register your cooperative's proprietary feed materials once. They are automatically injected into every farmer's ration formulated with your API key — no extra steps needed from the farmer.

GET /api/enterprise/ingredients

List all custom ingredients for this cooperative.

POST /api/enterprise/ingredients

Required Fields

FieldTypeDescription
namestringIngredient name
CP_pct_DMnumberCrude Protein % (Dry Matter basis)
EE_pct_DMnumberEther Extract / Fat % (DM basis)
crude_fibre_pctnumberCrude Fibre % (DM basis)
ash_pct_DMnumberAsh % (DM basis)
moisture_pctnumberMoisture %
price_INR_kg_freshnumberPrice per kg fresh weight (INR)

SDK Example

coop.create_enterprise_ingredient(
    name="Amul TMR Premix",
    CP_pct_DM=18.5,
    EE_pct_DM=3.2,
    crude_fibre_pct=14.0,
    ash_pct_DM=8.5,
    moisture_pct=10.0,
    price_INR_kg_fresh=22.0,
    category="Premix"
)

# List ingredients
ings = coop.list_enterprise_ingredients()
print(f"{len(ings)} ingredients registered")
PUT /api/enterprise/ingredients/{ing_id}

Update an existing enterprise ingredient. Only include fields to change.

DELETE /api/enterprise/ingredients/{ing_id}

Remove a cooperative ingredient. It will no longer appear in any formulation.

💡 Auto-injected into formulations
When any farmer calls /api/formulate using your pd_ API key, all active enterprise ingredients are automatically added to the AI optimizer's ingredient pool alongside the standard 200+ national ingredients.

Branding Configuration

Set your cooperative's brand identity. Used by tenant-loader.js and the WhatsApp bot welcome message.

GET /api/coop-admin/branding
POST /api/coop-admin/branding
FieldDescription
company_nameCooperative / company display name
taglineShort tagline shown on branded pages
logo_urlPublic HTTPS URL to logo image
primary_colorHex code (e.g. #B71C1C) — injected as --primary CSS variable
secondary_colorHex code — injected as --secondary CSS variable
contact_emailSupport email shown on branded pages
contact_phoneSupport phone number

📷 Image Uploads (Firebase Storage)

Upload your cooperative's logo and product thumbnails. Images are stored in Firebase Storage and the URL is automatically saved to the relevant record.

  • Accepted: JPEG, PNG, WebP
  • Max size: 2 MB
  • Auth: Firebase JWT or pd_ API key required
POST /api/coop-admin/upload/logo

Upload cooperative logo. URL is automatically saved to your branding record.

curl -X POST https://API_BASE/api/coop-admin/upload/logo \
  -H "Authorization: Bearer TOKEN" \
  -F "file=@/path/to/logo.png"
{"success": true, "url": "https://storage.googleapis.com/.../logo.png"}
POST /api/coop-admin/upload/product-image/{product_id}

Upload product thumbnail. URL is automatically saved to the product record.

curl -X POST https://API_BASE/api/coop-admin/upload/product-image/PRODUCT_ID \
  -H "Authorization: Bearer TOKEN" \
  -F "file=@/path/to/product.jpg"
{"success": true, "url": "https://storage.googleapis.com/.../products/PRODUCT_ID.jpg"}

WhatsApp Integration

Connect your cooperative's own Meta Business WhatsApp phone number so farmers receive formulation results directly from your number — not Pashudhan's. Pashudhan handles the AI engine; you own the farmer relationship.

GET /api/coop-admin/whatsapp
POST /api/coop-admin/whatsapp
FieldDescription
phone_number_idMeta Phone Number ID from your Business Manager
access_tokenPermanent System User Token from Meta Business Manager
verify_tokenToken you set when registering the webhook in Meta Developer Console
display_nameLabel for this WhatsApp number

Webhook URLs

Register these in your Meta App's Webhook configuration:

MethodURLPurpose
GET/api/whatsapp/coop/{coop_id}Webhook verification (Meta calls this once)
POST/api/whatsapp/coop/{coop_id}Incoming farmer messages
Your coop_id is your Firebase UID — visible on the Coop Admin Console Overview tab.

Webhook Event Payloads

Every outgoing webhook POST includes these headers from Pashudhan:

HeaderExample ValueDescription
X-Pashudhan-Signaturea3f9c1b2d4e...HMAC-SHA256 hexdigest of the raw body bytes, signed with your pd_ API key. No "sha256=" prefix.
X-Pashudhan-Eventformulation.completeEvent name
X-Pashudhan-Cooperative-Iduid_abc123Your cooperative's Firebase UID
X-Pashudhan-Timestamp1725436800Unix timestamp (seconds) of delivery attempt
User-AgentPashudhan-Webhook/1.0Identifies the sender
Content-Typeapplication/jsonAlways JSON
🔑 Your pd_ key serves two roles:
1. API authentication — sent in x-api-key header on every API call.
2. Webhook signing secret — used server-side as the HMAC-SHA256 key to sign outgoing webhook payloads. You verify incoming events with the same key.

All events share the same envelope structure:

formulation.complete

{
  "event":          "formulation.complete",
  "timestamp":      "2026-09-04T12:00:00.123Z",
  "cooperative_id": "uid_abc123",
  "api_version":    "2026-09",
  "data": {
    "formulation": {
      "farmer_id":       "918001234567",
      "animal_type":     "cow",
      "breed":           "HF_crossbred",
      "ration_cost_inr": 128.40,
      "formulation_id":  "rpt_xyz789"
    },
    "recommendations": []
  }
}

deficiency.detected

{
  "event":          "deficiency.detected",
  "timestamp":      "2026-09-04T12:00:01.456Z",
  "cooperative_id": "uid_abc123",
  "api_version":    "2026-09",
  "data": {
    "deficiencies": {
      "Ca_pct_DM": {
        "provided": 0.45,
        "required": 0.55,
        "gap_pct":  18.2
      }
    },
    "count": 1
  }
}

recommendation.triggered

{
  "event":          "recommendation.triggered",
  "timestamp":      "2026-09-04T12:00:01.789Z",
  "cooperative_id": "uid_abc123",
  "api_version":    "2026-09",
  "data": {
    "recommendations": [
      {
        "rule_id":         "rule_001",
        "nutrient":        "Calcium (Ca)",
        "nutrient_key":    "Ca_pct_DM",
        "gap_pct":         18.2,
        "type":            "product",
        "source_name":     "MilkBoost Mineral Mix",
        "suggested_qty_g": 100,
        "price_inr":       85,
        "link":            "https://example.com/milkboost",
        "message":         "Calcium deficit of 18.2%! Add 100g of MilkBoost daily.",
        "priority":        3,
        "standard":        "ICAR"
      }
    ],
    "count": 1
  }
}

farmer.registered

Fires the first time a new farmer phone number sends a WhatsApp message through your cooperative's number.

{
  "event":          "farmer.registered",
  "timestamp":      "2026-09-04T12:00:00.000Z",
  "cooperative_id": "uid_abc123",
  "api_version":    "2026-09",
  "data": {
    "farmer_id":   "918001234567",
    "channel":     "whatsapp"
  }
}

Envelope Fields — All Events

FieldTypeDescription
eventstringEvent name: formulation.complete | deficiency.detected | recommendation.triggered | farmer.registered
timestampstringISO 8601 UTC timestamp of when the event was created
cooperative_idstringYour Firebase UID — use this to route events in multi-tenant setups
api_versionstringAlways "2026-09" — check this for forward compatibility
dataobjectEvent-specific payload (see each event above)
⚡ Retry Policy: Pashudhan retries failed deliveries 3 times with exponential backoff: immediately → 5s → 30s → 5min. Your endpoint must return HTTP 2xx within 10 seconds or the attempt is marked failed. View all delivery logs on the Coop Admin Console → Webhooks tab.

🔑 API Key Management

Manage your Enterprise API keys programmatically. Each key can be independently named, rate-limited, and revoked without affecting other keys. All keys start with pd_ and are generated using cryptographically secure random bytes.

Your pd_ key is dual-purpose:
1. API authentication — pass as x-api-key header on every request.
2. Webhook signing secret — Pashudhan signs outgoing webhook payloads with HMAC-SHA256 using this key. Verify with the same key.
GET /api/enterprise/keys

List all API keys for this account — name, plan, monthly usage, limit, active status. Never returns the raw key value after creation.

Response

{
  "keys": [
    {
      "key_id":        "key_abc123",
      "name":          "Production Key",
      "key_prefix":    "pd_a1b2c3...",  // first 10 chars only
      "plan":          "ent_pro",
      "monthly_limit": 1000,
      "requests_used": 247,
      "active":        true,
      "created_at":    "2026-08-01T00:00:00Z"
    }
  ],
  "total_keys": 1
}
POST /api/enterprise/keys/generate

Generate a new API key. The full key is only returned once in this response — store it securely immediately.

Request Body

FieldTypeRequiredDescription
namestringREQHuman-readable label (e.g. "Production Key")
planstringOPTent_free | ent_pro | ent_promax. Default: ent_free

Response

{
  "key_id":   "key_abc123",
  "api_key":  "pd_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",  // full key — only shown once
  "name":     "Production Key",
  "plan":     "ent_free",
  "limit":    5
}
⚠️ Save the key immediately. After this response, only the first 10 characters are visible for identification. The full key cannot be retrieved again.
POST /api/enterprise/keys/{key_id}/rename

Rename a key for identification purposes. Pass {"name": "New Name"}.

DELETE /api/enterprise/keys/{key_id}

Permanently revoke an API key. Irreversible — all requests using this key will immediately return 401 Unauthorized. Usage history is preserved.

SDK Example — Key Management

import pashudhan_ai

client = pashudhan_ai.Client(api_key="pd_your_admin_key")

# List all keys
keys = client.list_api_keys()
for k in keys:
    print(f"{k['name']:20} {k['plan']:12} {k['requests_used']}/{k['monthly_limit']} calls")

# Generate a new key (returns full key ONCE)
new_key = client.generate_api_key(name="Mobile App Key", plan="ent_pro")
print(f"Key: {new_key['api_key']}")   # store this now — never shown again
print(f"ID:  {new_key['key_id']}")

# Rename a key
client.rename_api_key(new_key["key_id"], name="Production Mobile")

# Revoke a key
client.revoke_api_key(new_key["key_id"])   # irreversible

💳 Plan Subscription

POST /api/enterprise/subscribe

Upgrade or downgrade a key's plan tier. Plan takes effect immediately.

FieldTypeRequiredDescription
planstringREQent_free | ent_pro | ent_promax

Plan Tiers

PlanMonthly LimitPriceDescription
ent_free5 calls/moFreeDevelopment & testing
ent_pro1,000 calls/mo₹3,000/moProduction cooperative
ent_promax5,000 calls/mo₹10,000/moLarge cooperative / reseller

📊 Billing & Usage

GET /api/coop-admin/billing

Retrieve full billing status including all API keys, current usage, reset date, and plan details.

Response

{
  "plan":               "ent_pro",
  "monthly_limit":      1000,
  "requests_used":      247,
  "requests_remaining": 753,
  "reset_date":         "2026-10-01",
  "keys": [
    {
      "key_id":        "key_abc123",
      "name":          "Production Key",
      "key_prefix":    "pd_a1b2c3...",
      "requests_used": 247,
      "active":        true
    }
  ]
}

SDK Example

billing = client.cooperative.get_billing()
print(f"Plan:      {billing['plan']}")
print(f"Used:      {billing['requests_used']}/{billing['monthly_limit']}")
print(f"Remaining: {billing['requests_remaining']}")
print(f"Resets:    {billing['reset_date']}")

📋 Webhook Logs & Test

GET /api/coop-admin/webhooks/{webhook_id}/logs

Retrieve the last 50 delivery log entries for a webhook endpoint. Use to debug failed deliveries.

Response

{
  "logs": [
    {
      "timestamp":   "2026-09-04T12:00:01Z",
      "event":       "formulation.complete",
      "status_code": 200,
      "duration_ms": 124,
      "attempt":     1,
      "success":     true
    },
    {
      "timestamp":   "2026-09-04T11:00:01Z",
      "event":       "deficiency.detected",
      "status_code": 503,
      "duration_ms": 10000,
      "attempt":     3,
      "success":     false
    }
  ]
}
POST /api/coop-admin/webhooks/{webhook_id}/test

Send a test ping to your endpoint. Verifies reachability without using an API credit. Rate limited to 10/minute.

Response

{
  "status_code": 200,
  "duration_ms": 89,
  "success":     true
}

SDK Example — Webhook Operations

# List all webhooks
webhooks = client.cooperative.list_webhooks()
for wh in webhooks:
    print(f"{wh['url']} → events: {wh['events']}")

# Test a webhook (check reachability)
result = client.cooperative.test_webhook(webhooks[0]["id"])
print(f"Test: {result['status_code']} in {result['duration_ms']}ms")

# View delivery logs
logs = client.cooperative.get_webhook_logs(webhooks[0]["id"])
for log in logs:
    status = "✓" if log["success"] else "✗"
    print(f"  {status} {log['timestamp']} {log['event']} → {log['status_code']} ({log['duration_ms']}ms, attempt {log['attempt']})")

# Delete a webhook + all its logs (no orphaned data)
client.cooperative.delete_webhook(webhooks[0]["id"])

🐍 Python SDK — Cooperative Platform

SDK v0.5.0: The client.cooperative sub-client wraps all /api/coop-admin/* endpoints. Upgrade: pip install --upgrade pashudhan_ai

Full Cooperative Setup in Python

import pashudhan_ai

client = pashudhan_ai.Client(api_key="pd_your_enterprise_key")

# ── 1. Custom branding ──────────────────────────────────────────────
client.cooperative.set_branding(
    company_name   = "Amul Dairy Tech",
    tagline        = "Smart Nutrition for Every Farmer",
    logo_url       = "https://cdn.amul.com/logo.png",
    primary_color  = "#B71C1C",
    secondary_color= "#F57F17",
    contact_email  = "support@amuldairy.com",
    contact_phone  = "+91-2692-258506",
)

# ── 2. WhatsApp configuration ────────────────────────────────────────────
client.cooperative.set_whatsapp(
    phone_number_id = "123456789012345",
    access_token    = "EAABwzLixnjY...",  # Meta System User Token
    verify_token    = "my_custom_verify_secret",
    display_name    = "Amul Krishi Sahayak",
)

# ── 3. Add products to your catalog ─────────────────────────────────────
prod = client.cooperative.add_product(
    name           = "Amul MilkBoost Mineral Mix",
    sku            = "AMBMM-500",
    category       = "mineral_supplement",
    target_nutrient= "Ca_pct_DM",
    dose_g_per_day = 100,
    price_inr      = 85,
    product_link   = "https://shop.amul.com/milkboost",
    description    = "Premium Ca-P mix for dairy cows",
)
print(prod["id"])  # "prod_abc123"

# ── 4. Create recommendation rules ──────────────────────────────────────
client.cooperative.add_rule(
    nutrient_key       = "Ca_pct_DM",
    trigger_condition  = "deficit_pct_above",
    threshold          = 10,           # fires if Ca > 10% short
    recommendation_type= "product",
    product_id         = prod["id"],
    product_name       = "Amul MilkBoost Mineral Mix",
    product_link       = "https://shop.amul.com/milkboost",
    suggested_qty_g    = 100,
    message_template   = "Ca deficit {gap_pct}%! Add {qty}g of {source} daily.",
    priority           = 3,
)

# ── 5. Register an outgoing webhook ─────────────────────────────────────
client.cooperative.add_webhook(
    url         = "https://api.amuldairy.com/events",
    events      = ["*"],
    description = "Main ERP — all events",
)
client.cooperative.add_webhook(
    url         = "https://sms.amul.com/deficiency-alerts",
    events      = ["deficiency.detected"],
    description = "SMS alerts — deficiency only",
)

# ── 6. Retrieve analytics ────────────────────────────────────────────────
dashboard = client.cooperative.get_dashboard()
print(f"Farmers served: {dashboard['total_farmers']}")
print(f"API calls: {dashboard['api_calls_used']}/{dashboard['api_calls_limit']}")

# ── 7. View webhook delivery logs ────────────────────────────────────────
webhooks = client.cooperative.list_webhooks()
for wh in webhooks:
    logs = client.cooperative.get_webhook_logs(wh["id"])
    print(f"{wh['url']}: {len(logs)} deliveries")

Verify Incoming Webhooks (Server-Side)

Critical: The pd_ API key you use for API calls is also the HMAC signing key for webhooks. The X-Pashudhan-Signature header is a raw hex string — not prefixed with sha256=.
# Using the SDK helper (recommended)
from pashudhan_ai import WebhookVerifier

verifier = WebhookVerifier(api_key="pd_your_enterprise_key")

# In your Flask/FastAPI/Django view:
def my_webhook_handler(request):
    raw_body = request.get_data()  # raw bytes — do NOT parse first
    signature = request.headers.get("X-Pashudhan-Signature", "")

    if not verifier.verify(raw_body, signature):
        return {"error": "Invalid signature"}, 401

    event = request.json  # now safe to parse
    if event["event"] == "recommendation.triggered":
        recs = event["data"]["recommendations"]
        for rec in recs:
            print(f"Push to farmer: {rec['message']} → {rec['link']}")
    return {"ok": True}

# Manual verification (without SDK):
import hmac, hashlib

def verify_pashudhan_webhook(raw_body_bytes, signature_header, pd_api_key):
    expected = hmac.new(
        pd_api_key.encode("utf-8"),
        raw_body_bytes,
        hashlib.sha256
    ).hexdigest()  # raw hex — NO "sha256=" prefix
    return hmac.compare_digest(expected, signature_header)

Formulation API — B2B Response (with Recommendations)

When a formulation is called with a cooperative's pd_ API key, the response includes extra fields injected by your recommendation rules:

{
  "formulations": [...],  // same as standard API
  "cooperative_recommendations": [
    {
      "rule_id":         "rule_001",
      "nutrient":        "Calcium (Ca)",
      "gap_pct":         18.2,
      "type":            "product",
      "source_name":     "MilkBoost Mineral Mix",
      "suggested_qty_g": 100,
      "price_inr":       85,
      "link":            "https://shop.amul.com/milkboost",
      "message":         "Ca deficit 18.2%! Add 100g daily.",
      "priority":        3,
      "standard":        "ICAR"
    }
  ],
  "cooperative_id": "uid_abc123"
}

📊 Nutrient Key Reference

Use these exact keys in nutrient_key fields when creating recommendation rules.

KeyLabelUnitStandard
TDN_pct_DMTotal Digestible Nutrients%NASEM/ICAR
NEL_Mcal_kgNet Energy for LactationMcal/kgNASEM/ICAR
CP_pct_DMCrude Protein%NASEM/ICAR
RUP_pct_CPRumen Undegradable Protein% of CPNASEM
NDF_pct_DMNeutral Detergent Fiber%NASEM/ICAR
ADF_pct_DMAcid Detergent Fiber%NASEM/ICAR
Ca_pct_DMCalciumgNASEM/ICAR
P_pct_DMPhosphorusgNASEM/ICAR
Mg_pct_DMMagnesiumgNASEM/ICAR
K_pct_DMPotassiumgNASEM/ICAR
Na_pct_DMSodiumgNASEM/ICAR
Zn_mg_kgZincmg/kgNASEM/ICAR
Cu_mg_kgCoppermg/kgNASEM/ICAR
Se_mg_kgSeleniummg/kgNASEM/ICAR
vit_A_IU_kgVitamin AIU/kgNASEM/ICAR
vit_D_IU_kgVitamin DIU/kgNASEM/ICAR
vit_E_IU_kgVitamin EIU/kgNASEM/ICAR
EE_pct_DMEther Extract / Fat%NASEM/ICAR
Standards routing: Nutrient gaps are automatically evaluated against ICAR-NDRI for Indian indigenous breeds (Sahiwal, Gir, Murrah, Mehsana, etc.) and NASEM 2021 for all crossbreeds (HF, Jersey), Western breeds, and USA animals. Your recommendation rules are standards-agnostic — they evaluate the gap values regardless of which standard produced them.