Image diagnosis on the live FildraAI API with honest endpoint status and audience boundaries

The legacy /api/v1/diagnose gateway has been retired. The active image-diagnosis path is synchronous: stage the image with a presigned upload, then POST /api/v1/image_diagnosis/infer/basic with the raw_key and receive HTTP 200 with the predictions inline (no polling). Raw-key image diagnosis, metadata, ML crop recommendation, and map overview routes are validated in production.

base: https://api.fildraai.com
user and service key audiences
4 diagnosis crops
raw-key routes validated

API Reference

Production endpoint status

Use this page as the current integration contract, not placeholder documentation. It separates file-upload routes from staged-image routes and calls out what was actually observed on the live API.

POST /api/v1/image_diagnosis/infer/basic

Active user-key image-diagnosis endpoint, replacing the retired /api/v1/diagnose gateway. It is synchronous: send a JSON body with plant + raw_key and receive HTTP 200 with the predictions array inline. There is no job to poll.

Authentication
X-Api-Key: YOUR_API_KEY
Alternative: Authorization: Bearer YOUR_API_KEY
Base URL
https://api.fildraai.com
Audience
Active production route for user-key clients. Synchronous: returns HTTP 200 with the predictions inline.
Request Method
POST with a JSON body (application/json)
Image input
Pass raw_key from a presigned upload (see below). Direct multipart to this endpoint is not supported.
Query Parameters
plant (required), top_k (optional), locale (optional)
Supported Plants
cassava, maize, rice, tomato
Locale Resolution
Priority: ?locale=...Accept-Languageen-us
Query parameter takes priority if both are set

Query Parameters

Parameter Type Required Description
plant String Yes Plant type to diagnose. Must be one of: cassava, maize, rice, tomato.
top_k Integer No Number of predictions to return. Accepted range is 1–10. If omitted, the service uses 3.
locale String No Preferred response locale. Common examples include en-us and zh-tw. Unsupported values are rejected by the diagnosis service.
Implementation note: this is the active public user-key endpoint, which replaced the retired synchronous /api/v1/diagnose gateway. User keys and trusted server-side keys use the same raw_key contract (stage the image first, then infer). Returns HTTP 200 with the predictions inline, synchronously.

Additional Diagnosis Paths

Staged-image diagnosis routes

These endpoints diagnose images that are already staged in storage. Production validation on May 13, 2026 returned 200 for both user keys and server keys. Server-side products should still keep raw storage keys and API secrets on the backend, not in browsers or mobile clients.

POST /api/v1/image_diagnosis/infer/basic

Use this when your backend already has a staged object key. The request body is JSON and the response returns ranked predictions with Grad-CAM URLs but without knowledge enrichment.

Field Type Required Description
plantStringYescassava, maize, rice, or tomato.
raw_keyStringYesPreviously staged storage key, for example uploads/diagnose/user-123/maize/sample.jpg.
localeStringNoResponse locale. Falls back to request locale handling when omitted.
job_idStringNoOptional caller-supplied correlation id. Generated server-side if omitted.
top_kIntegerNoPrediction count. Defaults to 3, capped at 10.
countryStringNoReserved for future geographic filtering and logging.
POST /api/v1/image_diagnosis/infer/with_knowledge

This is the knowledge-backed variant of raw-key diagnosis. It returns the same prediction core plus a knowledge object per prediction, with section filtering and resolved citations already embedded where applicable.

Field Type Required Description
plantStringYesCrop scope for inference and profile resolution.
raw_keyStringYesStaged object key for the image already stored by your backend flow.
sectionsList of strings or CSV stringNoProfile sections to keep. Examples: ["visual_profile","lookalikes"] or "visual_profile,decision_support".
lookalikesBooleanNoAlias flag that expands to the matching section set.
visual_profileBooleanNoAlias flag for visual explanation sections.
decision_supportBooleanNoAlias flag for management guidance sections.
Allowed sections: cause, linkage, model_integration, visual_profile, lookalikes, decision_support, progression_profile, geographic_applicability, assistant_contract, evidence, evidence_summary, data_quality, validation_warnings.

Usage & Limits

Credits and runtime limits

API-key billing is credit-based. Playground usage is enforced by the backend identity service and is separate from developer API-key credits.

Guest playground
Backend managed
Guest playground requests are signed for backend allowance checks. The website no longer stores authoritative guest counters in the Flask session.
Authenticated playground
Backend managed
Authenticated playground requests use signed identity headers. The account page treats playground usage as informational until a backend status endpoint is wired in.
API key quota
50 included API credits / month
Paid API top-up credits are available after the included monthly API allowance is exhausted.

How usage is calculated

  • Guest and authenticated playground windows are separate from API key usage.
  • Playground quota enforcement lives in the API backend, not in the website billing table.
  • Included API credits are counted against the current calendar month; paid top-up credits do not reset monthly.
  • Clients should handle authentication, quota, and validation failures explicitly rather than assuming every non-200 response is transient.
API credit access: image diagnosis costs 3 credits per call; crop/yield ML and text translation cost 1; audio is metered by length; knowledge-base reads are free. Plans include a monthly allowance (Free 25, Basic 150, Pro 600) and top-up packs that don't expire, see the pricing page for the full table.

Code Examples

Integration Examples

Use live production URLs. Do not replace the host with a placeholder domain, and do not send service keys to browsers or mobile clients.

cURL, Basic Request
# Active user-key endpoint. Synchronous: returns HTTP 200 with the predictions inline.
# The image must already be staged (see "Send an image you have on hand" below); pass its raw_key.
curl -X POST "https://api.fildraai.com/api/v1/image_diagnosis/infer/basic" \
  -H "X-Api-Key: YOUR_USER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plant": "rice", "raw_key": "uploads/diagnose/your-staged-key.jpg", "top_k": 3, "locale": "en-us"}'
Python, Raw-Key Basic Inference
# Server-side inference when the image is already staged in storage
import requests

base_url = "https://api.fildraai.com"
api_key = "YOUR_SERVER_API_KEY"

url = f"{base_url}/api/v1/image_diagnosis/infer/basic"
payload = {
    "plant": "rice",
    "raw_key": "uploads/diagnose/some-user/rice/some-file.jpg",
    "locale": "en-us",
    "job_id": "demo-job-001",
    "top_k": 3,
    "country": "zambia"
}
headers = {
    "X-Api-Key": api_key,
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()

result = response.json()
print(result)
JavaScript, Knowledge-Enriched Diagnosis
// Server-side diagnosis + filtered knowledge sections
async function diagnoseWithKnowledge(rawKey) {
  const response = await fetch("https://api.fildraai.com/api/v1/image_diagnosis/infer/with_knowledge", {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': 'YOUR_SERVER_API_KEY'
    },
    body: JSON.stringify({
      plant: 'tomato',
      raw_key: rawKey,
      locale: 'en-us',
      top_k: 2,
      sections: ['visual_profile', 'lookalikes', 'decision_support'],
      lookalikes: true
    })
  });

  return await response.json();
}

Send an image you have on hand (upload then infer)

Every client uses the same raw_key path. If your client has the image file in hand (a mobile app, a script, or Postman), first request a presigned URL from POST /api/v1/image_diagnosis/uploads/presign, PUT the bytes to it, then call infer with the returned raw_key. Posting the file directly to the infer endpoint as multipart is not supported (it returns 400). The examples below show that upload-then-infer sequence.

Python, Upload then Infer (presign, PUT, infer)
import requests

base = "https://api.fildraai.com/api/v1"
headers = {"X-Api-Key": "YOUR_USER_API_KEY"}
img_path = "/absolute/path/to/maize_leaf.jpg"

# 1) Ask for a presigned PUT URL (free).
presign = requests.post(
    f"{base}/image_diagnosis/uploads/presign",
    headers=headers,
    json={"content_type": "image/jpeg", "size_bytes": 204800},
    timeout=30,
).json()
put_url, raw_key = presign["put_url"], presign["raw_key"]

# 2) PUT the raw bytes straight to storage (no API key on this request).
with open(img_path, "rb") as fh:
    requests.put(put_url, data=fh, headers={"Content-Type": "image/jpeg"}, timeout=60).raise_for_status()

# 3) Infer with the returned raw_key (JSON body, not multipart).
response = requests.post(
    f"{base}/image_diagnosis/infer/basic",
    headers=headers,
    json={"plant": "maize", "raw_key": raw_key, "top_k": 3, "locale": "en-us"},
    timeout=60,
)
response.raise_for_status()
print(response.json())
JavaScript, Upload then Infer (presign, PUT, infer)
// Works in browsers and Node 18+ (built-in fetch).
const base = 'https://api.fildraai.com/api/v1';
const headers = { 'X-Api-Key': 'YOUR_USER_API_KEY' };

async function diagnoseImage(file) {
  // 1) Presign (free).
  const presign = await (await fetch(`${base}/image_diagnosis/uploads/presign`, {
    method: 'POST',
    headers: { ...headers, 'Content-Type': 'application/json' },
    body: JSON.stringify({ content_type: file.type || 'image/jpeg', size_bytes: file.size }),
  })).json();

  // 2) PUT the bytes to storage (no API key on this request).
  await fetch(presign.put_url, { method: 'PUT', headers: { 'Content-Type': file.type || 'image/jpeg' }, body: file });

  // 3) Infer with the raw_key (JSON body, not multipart).
  const response = await fetch(`${base}/image_diagnosis/infer/basic`, {
    method: 'POST',
    headers: { ...headers, 'Content-Type': 'application/json' },
    body: JSON.stringify({ plant: 'maize', raw_key: presign.raw_key, top_k: 3, locale: 'en-us' }),
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}

Test in Postman

Postman is the fastest way to verify your API key and image work before wiring up code. The steps below mirror what the Python and JavaScript snippets do above: presign, PUT the bytes, then infer with the raw_key. Posting the image straight to the infer endpoint as form-data is not supported and returns 400.

  1. Request a presigned URL. Method POST, URL https://api.fildraai.com/api/v1/image_diagnosis/uploads/presign. Under Headers add X-Api-Key (your user-audience key) and Content-Type: application/json. Under Body select raw + JSON and send {"content_type":"image/jpeg","size_bytes":204800}. The response contains put_url and raw_key.
  2. Upload the image bytes. New request: method PUT, URL = the put_url from step 1 (no API key on this request). Add header Content-Type: image/jpeg. Under Body select binary and pick your JPEG. Send and expect 200.
  3. Run inference. New request: method POST, URL https://api.fildraai.com/api/v1/image_diagnosis/infer/basic. Headers X-Api-Key + Content-Type: application/json. Under Body select raw + JSON and send {"plant":"maize","raw_key":"PASTE_raw_key_FROM_STEP_1","top_k":3,"locale":"en-us"}.
  4. Read the result. A successful response returns 200 with a predictions array. Common failures: 401 (key missing/wrong), 403 (key audience or scope mismatch), 400 (plant not in cassava/maize/rice/tomato, or the image posted directly to infer as form-data, which is not supported).

Screenshots below, drop the captured PNGs into /static/img/docs/postman/ with the filenames shown.

Screenshot 1 Presign request (POST .../uploads/presign) Show Postman with POST selected, the presign URL pasted in, and the raw JSON body. Filename: postman_diagnosis_01_url.png
Screenshot 2 Adding the X-Api-Key header Headers tab with one row visible: X-Api-Key + your masked key value. Filename: postman_diagnosis_02_header.png
Screenshot 3 Infer request: raw JSON body with plant + raw_key Body tab, raw + JSON selected, showing plant and the raw_key from the presign step. Filename: postman_diagnosis_03_formdata.png
Screenshot 4 200 response with predictions Response pane showing 200 status + pretty-printed JSON with a predictions array. Filename: postman_diagnosis_04_response.png
Tip: save your API key as a Postman environment variable (e.g. {{FILDRA_USER_KEY}}) so you don't paste it into every request and so screenshots you share don't leak it.

Response Examples

Successful response

The raw-key diagnosis response currently returns the diagnosis fields directly. It does not include a top-level success flag or gateway block.

Example Success Response
{
  "plant": "maize",
  "locale": "en-us",
  "kb_locale": "en-us",
  "job_id": "2ae7df30-3e3b-4d7b-b89d-6626a3d08b02",
  "top_k": 3,
  "raw_key": "uploads/<id>.jpg",
  "kb_available_locales": ["en-us", "es-ar", "fr-af", "pt-ao", "sw-ke", "zh-cn", "zh-tw"],
  "predictions": [
    {
      "class_index": 4,
      "label": "grey_leaf_spot",
      "confidence": 0.9412,
      "gradcam_key": "diagnostics/gradcams/.../pred_1.png",
      "gradcam_url": "https://signed-storage-url.example/pred_1.png"
    },
    {
      "class_index": 2,
      "label": "common_rust",
      "confidence": 0.0418,
      "gradcam_key": "diagnostics/gradcams/.../pred_2.png",
      "gradcam_url": "https://signed-storage-url.example/pred_2.png"
    }
  ],
  "inference_plants": ["cassava", "maize", "rice", "tomato"],
  "supported_plants": ["cassava", "maize", "rice", "tomato"]
}

Error response

Validation, authentication, quota, and server failures should all be handled explicitly in client code.

Example Error Response
{
  "success": false,
  "error": "bad_request",
  "message": "Unsupported plant: 'wheat'. Supported plants: cassava, maize, rice, tomato",
  "status_code": 400
}
Implementation note: do not hard-code an exact prediction schema unless your deployment contract guarantees it. Safely parse top-level diagnosis fields and handle validation, audience, quota, and upstream failures separately from model output.
Knowledge-backed variant: the response from /api/v1/image_diagnosis/infer/with_knowledge keeps the same top-level fields and adds canonical_disease_key plus a per-prediction knowledge object containing the filtered profile sections you requested.

Common error classes

  • 400 Bad Request: missing query parameter, unsupported plant, invalid image type, empty file, or file too large
  • 401 Unauthorized: missing API key or invalid API key
  • 403 Forbidden: wrong audience, missing scope, or API not allowed for the key
  • Quota exceeded: usage rejected after the configured guest, authenticated, or API-key quota is exhausted
  • 500 Internal Server Error: storage or diagnosis engine failure

Common mistakes and good to know

  • Do not post the image as multipart to the infer endpoint. It returns 400. Presign, PUT the bytes, then infer with the returned raw_key.
  • The raw_key comes from the presign response, not from your local filename.
  • The image cap is 15 MiB. The presign request rejects a larger size_bytes with 400, so validate the file size before uploading.
  • locale defaults to en-us when omitted, and top_k is clamped to a maximum of 10.
  • gradcam_url is a signed link that expires in about 15 minutes. Fetch or cache the overlay before then if you need it offline.
  • job_id is returned for your logs and tracking. It is a correlation id; you do not need to send one.

Need more diagnosis API calls?

Buy API top-up credits or choose an API plan from your account billing page. Credits are granted only after payment success is confirmed.