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.
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.
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
- Go to your API Dashboard and generate an Enterprise API Key.
- Click the Download MCP Config button next to your key.
- This downloads a
pashudhan_mcp_config.jsonfile. - 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
- Windows:
- Restart Claude Desktop. The AI will now have access to the Pashudhan Formulation Tools!
Python SDK — Install
pashudhan_ai package wraps the REST API with a clean, Pythonic interface. No need to handle HTTP headers or JSON manually.
Install via pip
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
The core endpoint. Returns up to 4 optimized ration strategies in JSON.
Request Body
| Parameter | Type | Description |
|---|---|---|
| animal_type required | string | cow, buffalo, heifer, bull |
| breed required | string | e.g. HF_crossbred, Sahiwal, Murrah |
| body_weight_kg required | number | Body weight in kg (e.g. 450) |
| milk_yield_kg_day required | number | Daily milk yield in kg |
| fat_pct required | number | Milk fat % (e.g. 3.5). Only used if lactating. |
| protein_pct optional | number | Milk protein % (e.g. 3.2). Default: breed avg. Only used if lactating. |
| production_stage required | string | lactating, dry, growing, pre_calving |
| dim optional | number | Days in Milk. Affects intake & ketosis risk scoring. |
| parity optional | number | Lactation number. Default 1. Used for lactating/dry. |
| days_pregnant optional | number | Days of pregnancy. Triggers fetal energy demands if > 60. |
| bcs optional | number | Body Condition Score (1-5). Default 3.0. |
| health_flags optional | array | Strings e.g. ["ketosis_history", "steaming_up"] |
| state required | string | State or region (e.g. Punjab, Gujarat) |
| month required | number | Month 1–12 for seasonal ingredient filter |
| use_ai optional | boolean | Enable AI Nutritionist critique (Pro/ProMax only) |
| location optional | string | City or lat,lon for live THI heat stress adjustment |
| farm_mode optional | string | Optimization 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 optional | number | Milk 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 }
}
}
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.
Formulates for an entire herd. Deducts 4 API credits per call.
Request Body
| Parameter | Type | Description |
|---|---|---|
| groups required | array | List of animal group dictionaries (each containing entries) |
| state required | string | State or region (e.g. Punjab, Gujarat) |
| month required | number | Month 1–12 for seasonal ingredient filter |
| farmer_ingredients optional | array | List 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."
}
}
]
}
Returns all available subscription plans. No authentication required.
curl https://pashudhan-nutri-ai.web.app/api/plans
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
}
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.
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.
| Parameter | Type | Description |
|---|---|---|
| biogas_mode optional | string | "biogas_dung_max" (default) or "balanced_biogas". The system auto-overrides to balanced_biogas for lactating / late-pregnant animals. |
| biogas_price_per_m3 optional | number | CBG or biogas sale price in ₹/m³. Defaults to saved enterprise setting or ₹35/m³. |
| milk_price_inr optional | number | Milk 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.) | ||
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.
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.
Read or update enterprise-level biogas configuration: plant capacity (m³), CBG price (₹/m³), slurry price (₹/kg), and default farm mode.
/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.
/api/enterprise/biogas/settings
Get current biogas configuration.
/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
/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']}")
Returns a list of supported countries and their configurations (currency, regions). No authentication required.
curl https://pashudhan-nutri-ai.web.app/api/countries
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.
• 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
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
| Parameter | Type | Description |
|---|---|---|
| name required | string | Name of your custom feed (e.g. My Special Pellets) |
| price_INR_kg_fresh required | number | Cost per kg fresh (e.g. 24.50) |
| moisture_pct required | number | Moisture percentage (e.g. 10.0) |
| CP_pct_DM required | number | Crude Protein % on Dry Matter basis |
| EE_pct_DM required | number | Ether Extract (Fat) % on Dry Matter basis |
| crude_fibre_pct required | number | Crude Fibre % on Dry Matter basis |
| ash_pct_DM required | number | Total 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
}
List all custom ingredients associated with your enterprise key.
Update an existing custom ingredient. The server will recalculate derived values if you change the primary macro-nutrients.
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.
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.
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.
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:
| Group | Animals | Key Nutritional Priority |
|---|---|---|
| High-yield Lactating | Fresh cows + cows giving >20 L/day (DIM 5–100) | Max NEL density, bypass protein |
| Mid-yield Lactating | Cows giving 10–20 L/day (DIM 100–200) | Balanced cost/performance |
| Late Lactation / Dry Off | Cows giving <10 L/day (DIM >200) | Reduce energy, prevent over-conditioning |
| Close-Up Dry | 21 days before calving | Anionic diet, low Ca, monitor BCS |
| Far-Off Dry | 60–22 days before calving | Maintenance only, high roughage |
| Heifers | 0–24 months | Growth, 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:
| Stage | Target BCS | Action if Below | Action if Above |
|---|---|---|---|
| Calving | 3.0 – 3.25 | Increase concentrate pre-calving | Reduce energy in dry period |
| Early Lactation (DIM 0–60) | 2.5 – 3.0 | Add bypass fat, limit milk goal | Monitor for fatty liver |
| Peak / Mid Lactation | 2.75 – 3.25 | Increase energy density | Normal range |
| Drying Off | 3.0 – 3.5 | Supplement energy, check parasites | Restrict concentrate |
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.
| Season | Months | Key Available Feeds | Watch Out For |
|---|---|---|---|
| Summer (Garmi) | March – June | Maize silage (if stored), dry straw, cotton seed | Heat stress (THI > 72), water scarcity, low green fodder — supplement bypass fat |
| Monsoon (Barsaat) | July – September | Fresh green fodder (napier, bajra, maize), cheap prices | Mycotoxin contamination in wet silage, low DM content in fresh fodder |
| Post-Monsoon (Kharif Harvest) | October – November | Maize grain, groundnut cake, soyabean meal — peak availability and lowest prices | Aflatoxin in groundnut products post-rains — insist on tested batches |
| Winter (Sardi) | December – February | Wheat bran, mustard cake, berseem/lucerne (premium green fodder) | Optimal DMI season — highest milk yields expected; maximise production |
| Spring (Rabi Harvest) | March | Wheat straw (new crop), cheap bran | Transition 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.
400 Bad Request and a detailed ValidationError explaining the biological mismatch.
| Parameter | Constraint | Description |
|---|---|---|
body_weight_kg | 10 to 1500 | Captures from small newborn calves up to mature heavy bulls. |
milk_yield_kg_day | ≤ 100 | Maximum plausible physiological limit for daily yield. |
fat_pct | 0.5 to 15.0 | Ensures valid energy correction mathematics. |
protein_pct | 1.5 to 7.0 | Ensures valid milk protein limits. |
days_pregnant | ≤ 320 | Accommodates standard cow gestation (~285) and buffalo (~315). |
dim (Days in Milk) | ≤ 1000 | Prevents infinite lactation curves. |
parity | 1 to 20 | Biological limits on the number of lactations. |
bcs | 1.0 to 5.0 | Body condition scoring limits. |
age_months | ≤ 360 (30 years) | Absolute biological maximum age. |
| Stage Mismatch | Calf max 18 mo. Heifer max 48 mo. | The API will aggressively reject a 48-month-old calf, enforcing standard maturity timelines. |
| Health Flags Logic | Conditional | protein_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.
| Code | Meaning | Resolution |
|---|---|---|
| 200 | Success | Process the response normally. |
| 400 | Bad Request — invalid payload | Check required fields, animal count limits (<=15 for herd), valid month (1–12). |
| 401 | Unauthorised — missing or invalid API key | Verify the x-api-key header is included and the key is active. |
| 403 | Forbidden — plan limit exceeded or wrong role | Check your credit balance via GET /api/enterprise/status. Upgrade plan if needed. |
| 429 | Rate Limited — monthly quota exhausted | Monthly limit reset on the 1st. Upgrade to Pro API or Enterprise Max plan. |
| 500 | Server Error — formulation engine issue | Retry once. If persistent, contact support with the request body and timestamp. |
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.
dairy_id.POST /api/animals/register — Register Animal
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | REQ | Animal name or label (e.g. "Rani"). |
| animal_type | string | REQ | "cow" | "buffalo" | "heifer" | "bull" | "calf_weaned" |
| breed | string | REQ | Breed string (e.g. "Murrah_buffalo"). See GET /api/breeds. |
| current_stage | string | OPT | Production stage. Default: "mid_lactation". |
| parity | int | OPT | Number of calves born so far. Default: 1. |
| tag_id | string | OPT | Ear tag / farm ID number. |
| dairy_id | string | OPT | Farm/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
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
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.
| Field | Type | Required | Description |
|---|---|---|---|
| actual_milk_yield_kg | float | REQ | Actual milk yield (kg/day) from today's slip. |
| actual_fat_pct | float | REQ | Actual milk fat %. |
| actual_snf_pct | float | OPT | SNF % if available. |
| days_elapsed | int | OPT | Days since the ration was applied. Default: 7. |
| health_events | list | OPT | Observations e.g. ["normal"]. |
| bcs_score | float | OPT | Body condition score (1–5). |
| ration_id | string | OPT | Report 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.
ent_pro or ent_promax plan. Individual accounts see a single default farm.POST /api/dairies — Register a Dairy Farm
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | REQ | Farm name (e.g. "Singh Dairy — Ludhiana Unit 2"). |
| location | string | OPT | Farm 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
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
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.
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.
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)
| Feature | Firestore Path | Isolated? |
|---|---|---|
| Branding | users/{coop_id}/config/branding | ✅ Per-coop |
| WhatsApp credentials | users/{coop_id}/config/whatsapp | ✅ Per-coop, token never returned in full |
| Custom feed ingredients | enterprises/{coop_id}/custom_ingredients/ | ✅ Separate from B2C users |
| Product catalog | users/{coop_id}/products/ | ✅ Per-coop |
| Recommendation rules | users/{coop_id}/rules/ | ✅ Per-coop |
| Webhooks + logs | users/{coop_id}/webhooks/{id}/entries/ | ✅ Per-coop |
| Farmer analytics | users/{coop_id}/farmers/ | ✅ Per-coop |
| API keys | api_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.
/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
- Generate an Enterprise API key on the API Dashboard.
- Open the Coop Admin Console to configure branding, webhooks, rules, and products.
- 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. - 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. - Set up webhooks to receive
formulation.complete,deficiency.detected, andrecommendation.triggeredevents in your own database — any tech stack.
Returns the cooperative's branding config. Used by tenant-loader.js to inject custom styles into any page.
| Header | Required | Description |
|---|---|---|
x-api-key | REQ | Enterprise 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"
}
}
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.
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.
List all webhook endpoints.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | REQ | HTTPS endpoint to receive events |
events | array | REQ | ["formulation.complete","deficiency.detected","recommendation.triggered","farmer.registered","*"] |
description | string | OPT | Human-readable label |
active | bool | OPT | Default: true |
Send a test event to verify your endpoint is reachable. Returns delivery status and response time.
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.
target_nutrients →
the AI detects a deficiency → your product is shown with the purchase link. No rules, no thresholds to configure.
Fields
| Field | Type | Description |
|---|---|---|
name | string | Product display name |
sku | string | Internal SKU |
category | string | e.g. mineral_supplement, energy_supplement |
target_nutrients | array | List 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_link | string | Purchase/promo URL shown to the farmer when this product is recommended. Use your shop, Amazon, or affiliate link. |
description | string | Free text description |
active | bool | Whether to include in recommendations |
image_url | string | Product thumbnail URL (from Firebase Storage) |
🌿 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.
/api/enterprise/ingredients
List all custom ingredients for this cooperative.
/api/enterprise/ingredients
Required Fields
| Field | Type | Description |
|---|---|---|
| name | string | Ingredient name |
| CP_pct_DM | number | Crude Protein % (Dry Matter basis) |
| EE_pct_DM | number | Ether Extract / Fat % (DM basis) |
| crude_fibre_pct | number | Crude Fibre % (DM basis) |
| ash_pct_DM | number | Ash % (DM basis) |
| moisture_pct | number | Moisture % |
| price_INR_kg_fresh | number | Price 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")
/api/enterprise/ingredients/{ing_id}
Update an existing enterprise ingredient. Only include fields to change.
/api/enterprise/ingredients/{ing_id}
Remove a cooperative ingredient. It will no longer appear in any formulation.
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.
| Field | Description |
|---|---|
company_name | Cooperative / company display name |
tagline | Short tagline shown on branded pages |
logo_url | Public HTTPS URL to logo image |
primary_color | Hex code (e.g. #B71C1C) — injected as --primary CSS variable |
secondary_color | Hex code — injected as --secondary CSS variable |
contact_email | Support email shown on branded pages |
contact_phone | Support 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
/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"}
/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.
| Field | Description |
|---|---|
phone_number_id | Meta Phone Number ID from your Business Manager |
access_token | Permanent System User Token from Meta Business Manager |
verify_token | Token you set when registering the webhook in Meta Developer Console |
display_name | Label for this WhatsApp number |
Webhook URLs
Register these in your Meta App's Webhook configuration:
| Method | URL | Purpose |
|---|---|---|
| GET | /api/whatsapp/coop/{coop_id} | Webhook verification (Meta calls this once) |
| POST | /api/whatsapp/coop/{coop_id} | Incoming farmer messages |
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:
| Header | Example Value | Description |
|---|---|---|
X-Pashudhan-Signature | a3f9c1b2d4e... | HMAC-SHA256 hexdigest of the raw body bytes, signed with your pd_ API key. No "sha256=" prefix. |
X-Pashudhan-Event | formulation.complete | Event name |
X-Pashudhan-Cooperative-Id | uid_abc123 | Your cooperative's Firebase UID |
X-Pashudhan-Timestamp | 1725436800 | Unix timestamp (seconds) of delivery attempt |
User-Agent | Pashudhan-Webhook/1.0 | Identifies the sender |
Content-Type | application/json | Always JSON |
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
| Field | Type | Description |
|---|---|---|
event | string | Event name: formulation.complete | deficiency.detected | recommendation.triggered | farmer.registered |
timestamp | string | ISO 8601 UTC timestamp of when the event was created |
cooperative_id | string | Your Firebase UID — use this to route events in multi-tenant setups |
api_version | string | Always "2026-09" — check this for forward compatibility |
data | object | Event-specific payload (see each event above) |
🔑 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.
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.
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
}
Generate a new API key. The full key is only returned once in this response — store it securely immediately.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | REQ | Human-readable label (e.g. "Production Key") |
plan | string | OPT | ent_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
}
Rename a key for identification purposes. Pass {"name": "New Name"}.
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
Upgrade or downgrade a key's plan tier. Plan takes effect immediately.
| Field | Type | Required | Description |
|---|---|---|---|
plan | string | REQ | ent_free | ent_pro | ent_promax |
Plan Tiers
| Plan | Monthly Limit | Price | Description |
|---|---|---|---|
ent_free | 5 calls/mo | Free | Development & testing |
ent_pro | 1,000 calls/mo | ₹3,000/mo | Production cooperative |
ent_promax | 5,000 calls/mo | ₹10,000/mo | Large cooperative / reseller |
📊 Billing & Usage
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
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
}
]
}
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
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)
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.
| Key | Label | Unit | Standard |
|---|---|---|---|
TDN_pct_DM | Total Digestible Nutrients | % | NASEM/ICAR |
NEL_Mcal_kg | Net Energy for Lactation | Mcal/kg | NASEM/ICAR |
CP_pct_DM | Crude Protein | % | NASEM/ICAR |
RUP_pct_CP | Rumen Undegradable Protein | % of CP | NASEM |
NDF_pct_DM | Neutral Detergent Fiber | % | NASEM/ICAR |
ADF_pct_DM | Acid Detergent Fiber | % | NASEM/ICAR |
Ca_pct_DM | Calcium | g | NASEM/ICAR |
P_pct_DM | Phosphorus | g | NASEM/ICAR |
Mg_pct_DM | Magnesium | g | NASEM/ICAR |
K_pct_DM | Potassium | g | NASEM/ICAR |
Na_pct_DM | Sodium | g | NASEM/ICAR |
Zn_mg_kg | Zinc | mg/kg | NASEM/ICAR |
Cu_mg_kg | Copper | mg/kg | NASEM/ICAR |
Se_mg_kg | Selenium | mg/kg | NASEM/ICAR |
vit_A_IU_kg | Vitamin A | IU/kg | NASEM/ICAR |
vit_D_IU_kg | Vitamin D | IU/kg | NASEM/ICAR |
vit_E_IU_kg | Vitamin E | IU/kg | NASEM/ICAR |
EE_pct_DM | Ether Extract / Fat | % | NASEM/ICAR |