+ Added public data export API enhancements, tests, and customer documentation + Extended /api/v1 data export endpoints with richer session, records, area, and async export output + Added confirmed/fallback report values, client metadata, mapped area, over-spray, volume/apprate (string) units, and weather blocks + Normalized flowController to "No FC" and align record field names with playback output + Converted record wind speed output to knots, add Fligh Mater only record/export fields behind fm=true, and persist fm on export jobs + Added export status/area constants, HTTP 202 support, route-level API docs, and per-account export rate limiting support + Added comprehensive endpoint, format, and verification test coverage plus test-suite README + Added customer-facing data export design, integration, rate-limit, and documentation index guides + Updated README/DLQ docs and related documentation links to current HTTPS dashboard paths
26 KiB
AgMission Data Export API — Customer Integration Guide
Audience: Technical integrators, BI teams, data warehouse engineers
Version: 1.0
Last Updated: April 2026
Table of Contents
- Overview
- Quick Start
- Authentication
- API Endpoints
- Rate Limiting
- Data Formats
- Use Cases
- Error Handling
- Support & SLAs
Overview
The AgMission Data Export API provides programmatic access to spray application data for integration with business intelligence tools, data warehouses, and custom systems.
Capabilities
- Real-time session summaries — Coverage, timing, pilot, aircraft info (GET
/api/v1/jobs/:jobId/sessions) - Raw GPS trace records — Point-by-point telemetry with cursor pagination (GET
/api/v1/jobs/:jobId/sessions/:fileId/records) - Spray area polygons — GeoJSON boundaries for mapping (GET
/api/v1/jobs/:jobId/areas) - Async bulk export — CSV or GeoJSON for full data lake ingestion (POST/GET
/api/v1/jobs/:jobId/export)
Who Should Use This API
| Role | Use Case |
|---|---|
| BI Engineer | Power BI incremental refresh, Tableau connectors |
| Data Warehouse | Nightly batch loads, transformation pipelines |
| GIS Analyst | ArcGIS layer ingestion, spatial analysis |
| Compliance Officer | Audit trails, proof-of-application records |
| Agronomist | Yield correlation, efficacy analysis |
Architecture
graph TD
ext[Your System]
gw[AgMission API Gateway - Auth and Rate Limiting]
sess[GET /api/v1/jobs/:id/sessions]
recs[GET /api/v1/jobs/:id/sessions/:fid/records]
areas[GET /api/v1/jobs/:id/areas]
exp[POST /api/v1/jobs/:id/export]
stat[GET /api/v1/exports/:id]
dl[GET /api/v1/exports/:id/download]
db[(MongoDB - Applications and GPS Trace)]
ext -->|X-API-Key over HTTPS| gw
gw --> sess
gw --> recs
gw --> areas
gw --> exp
gw --> stat
gw --> dl
sess --> db
recs --> db
areas --> db
exp --> db
stat --> db
dl --> db
style ext fill:#e3f2fd
style gw fill:#f3e5f5
style db fill:#e8f5e9
Quick Start
1. Get an API Key
Contact your AgMission account manager or self-serve at https://agmission.agnav.com/api-keys:
ak_test_3v8x2j9kL4m5nQ6... (test key)
ak_live_7p2r9w4tY3h8k1... (production key)
2. List sessions for a job
JOB_ID=12345
API_KEY="ak_test_3v8x2j9kL4m5nQ6..."
curl -X GET "https://api.agmission.com/api/v1/jobs/${JOB_ID}/sessions" \
-H "X-API-Key: ${API_KEY}"
Response:
{
"jobId": 12345,
"clientId": "507f1f77bcf86cd799439055",
"clientName": "Fazenda São Paulo Ltda",
"mappedArea_ha": 48.5,
"reportConfirmed": false,
"areaSize_ha": 48.5,
"coverage_ha": 45.2,
"overSprayedPct": -6.8,
"appRate": 50,
"appRateUnit": "lit/ha",
"appRateConfirmed": null,
"sprayVolume": 2260,
"volumeUnit": "lit",
"useActualVolume": false,
"actualVolume": null,
"effectiveVolume": 2260,
"useCustomWeather": false,
"weather": null,
"data": [
{
"sessionId": "507f1f77bcf86cd799439011",
"fileName": "flight_20260422_001.log",
"startDateTime": "2026-04-22T09:00:00Z",
"endDateTime": "2026-04-22T11:30:00Z",
"totalFlightTime_s": 9000,
"totalSprayTime_s": 7200,
"totalTurnTime_s": 1800,
"totalSprayed_ha": 45.2,
"totalSprayMat": 2260,
"totalSprayMatUnit": "lit",
"avgSpraySpeed_ms": 39.5,
"appRate": 50,
"appRateUnit": "lit/ha",
"matType": "wet",
"flowController": "SatLoc G4",
"sprayOnLag_s": 0.2,
"sprayOffLag_s": 0.15,
"pulsesPerLiter": 1800,
"sprayZoneName": "Field A North",
"sprayZoneArea_ha": 25.0,
"files": [
{ "fileId": "507f1f77bcf86cd799439022", "name": "flight_20260422_001.log" }
],
"sessionPilotName": "John Smith",
"pilotId": "507f1f77bcf86cd799439033",
"pilotName": "John Smith",
"aircraftName": "AT-802F",
"aircraftTailNumber": "N1234AT",
"assignedDate": "2026-04-21T18:00:00Z",
"reportConfirmed": false,
"areaSize_ha": 48.5,
"coverage_ha": 45.2,
"appRateConfirmed": null,
"sprayVolume": 2260,
"volumeUnit": "lit",
"useActualVolume": false,
"actualVolume": null,
"effectiveVolume": 2260
}
]
}
3. Export to CSV
JOB_ID=12345
API_KEY="ak_test_3v8x2j9kL4m5nQ6..."
# Trigger export (async)
EXPORT_ID=$(curl -s -X POST "https://api.agmission.com/api/v1/jobs/${JOB_ID}/export" \
-H "X-API-Key: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{"format":"csv","units":"metric"}' \
| jq -r '.exportId')
echo "Export ID: $EXPORT_ID"
# Poll for completion
while true; do
STATUS=$(curl -s -X GET "https://api.agmission.com/api/v1/exports/${EXPORT_ID}" \
-H "X-API-Key: ${API_KEY}" \
| jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" = "ready" ]; then
break
fi
sleep 5
done
# Download
curl -X GET "https://api.agmission.com/api/v1/exports/${EXPORT_ID}/download" \
-H "X-API-Key: ${API_KEY}" \
-o "export_job${JOB_ID}.csv"
echo "Downloaded: export_job${JOB_ID}.csv"
Authentication
API Key Format
API keys are Bearer tokens supplied via the X-API-Key header (NOT Authorization header).
sequenceDiagram
participant Client as Your System
participant API as AgMission API
participant Auth as Auth Middleware
participant DB as ApiKey Store
Client->>API: GET /api/v1/jobs/123/sessions
Note over Client,API: Header: X-API-Key: ak_live_abc123...
API->>Auth: Verify key
Auth->>DB: Lookup by prefix first 8 chars
DB-->>Auth: Key candidate
Auth->>Auth: bcrypt.compare key vs stored hash
Auth-->>API: req.uid set to account owner
API-->>Client: 200 JSON response
DO NOT use Authorization: Bearer ak_test_... — This will fail!
# ✅ CORRECT
curl -H "X-API-Key: ak_test_3v8x2j9kL4m5nQ6..." \
https://api.agmission.com/api/v1/jobs/12345/sessions
# ❌ WRONG
curl -H "Authorization: Bearer ak_test_3v8x2j9kL4m5nQ6..." \
https://api.agmission.com/api/v1/jobs/12345/sessions
Key Management
- Create new keys at
https://agmission.agnav.com/api-keys - Rotate keys by creating new ones and disabling old ones
- Scope keys by job or account (coming soon)
- Revoke immediately if compromised
Security Best Practices
-
Never commit keys to version control — Use environment variables or secrets manager
export AGMISSION_API_KEY="ak_test_..." curl -H "X-API-Key: $AGMISSION_API_KEY" https://api.agmission.com/... -
Use HTTPS only — API endpoints enforce TLS 1.3+
-
Rotate keys quarterly — Implement key rotation in your automation
-
Monitor key usage — Check activity logs for suspicious patterns
API Endpoints
1. List Sessions
Endpoint: GET /api/v1/jobs/:jobId/sessions
Returns one summary per uploaded flight log file.
Parameters:
jobId(path) — Job ID (integer)
Response (200 OK):
{
"jobId": 12345,
"mappedArea_ha": 48.5,
"reportConfirmed": false,
"areaSize_ha": 48.5,
"coverage_ha": 45.2,
"overSprayedPct": -6.8,
"appRate": 50,
"appRateUnit": "lit/ha",
"appRateConfirmed": null,
"sprayVolume": 2260,
"volumeUnit": "lit",
"useActualVolume": false,
"actualVolume": null,
"effectiveVolume": 2260,
"useCustomWeather": false,
"weather": null,
"data": [
{
"sessionId": "507f1f77bcf86cd799439011",
"fileName": "flight_20260422_001.log",
"startDateTime": "2026-04-22T09:00:00Z",
"endDateTime": "2026-04-22T11:30:00Z",
"totalFlightTime_s": 9000,
"totalSprayTime_s": 7200,
"totalTurnTime_s": 1800,
"totalSprayed_ha": 45.2,
"totalSprayMat": 2260,
"totalSprayMatUnit": "lit",
"avgSpraySpeed_ms": 39.5,
"appRate": 50,
"appRateUnit": "lit/ha",
"matType": "wet",
"flowController": "SatLoc G4",
"sprayOnLag_s": 0.2,
"sprayOffLag_s": 0.15,
"pulsesPerLiter": 1800,
"sprayZoneName": "Field A North",
"sprayZoneArea_ha": 25.0,
"files": [
{ "fileId": "507f1f77bcf86cd799439022", "name": "flight_20260422_001.log" }
],
"sessionPilotName": "John Smith",
"pilotId": "507f1f77bcf86cd799439033",
"pilotName": "John Smith",
"aircraftName": "AT-802F",
"aircraftTailNumber": "N1234AT",
"assignedDate": "2026-04-21T18:00:00Z",
"reportConfirmed": false,
"areaSize_ha": 48.5,
"coverage_ha": 45.2,
"appRateConfirmed": null,
"sprayVolume": 2260,
"volumeUnit": "lit",
"useActualVolume": false,
"actualVolume": null,
"effectiveVolume": 2260
}
]
}
Confirmed vs Fallback Values:
When reportConfirmed: true, the applicator has manually confirmed spray records in Report Settings:
areaSize_ha,coverage_ha,appRate,actualVolume,weathercome from the report- Otherwise, system-calculated fallbacks are used
2. Get Records (Paginated GPS Trace)
Endpoint: GET /api/v1/jobs/:jobId/sessions/:fileId/records
Streams raw GPS points with cursor-based pagination.
Parameters:
jobId(path) — Job IDfileId(path) — Session/file IDstartingAfter(query) — Cursor for paginationlimit(query) — Records per page (default 500, max 2000)interval(query) — GPS thinning interval in seconds (float)
Example: Fetch 500 records, every 5 seconds
curl "https://api.agmission.com/api/v1/jobs/12345/sessions/507f1f77.../records?limit=500&interval=5" \
-H "X-API-Key: ak_test_..."
Response (200 OK):
{
"data": [
{
"timeUtc": "2026-04-22T09:00:15Z",
"gpsTime": 1745312415,
"lat": 40.7128,
"lon": -74.0060,
"alt": 150.5,
"grSpeed": 39.8,
"heading": 180,
"sprayStat": 1,
"flowRateApplied": 48.5,
"appRateApplied": 49.3,
"windSpeed_kt": 6.22,
"windDir_deg": 225,
"temp_c": 22.5,
"humidity_pct": 65
}
],
"hasMore": true,
"startingAfter": "507f191e810c19729de8605f",
"endingBefore": "507f1f77bcf86cd799439011"
}
Cursor field meanings:
startingAfter— pass as query param to get the next pageendingBefore— pass as query param to get the previous pagehasMore: false+ nostartingAftermeans you have reached the last page
Pagination:
# Get next page
curl "https://api.agmission.com/api/v1/jobs/12345/sessions/507f1f77.../records?startingAfter=507f191e810c19729de8605f" \
-H "X-API-Key: ak_test_..."
Use Cases:
- Power BI incremental refresh: Use
startingAfterto fetch only new records since last sync - Lightweight queries: Use
interval=5to reduce data volume by 5x - Real-time dashboards: Long-poll this endpoint every 10 seconds
3. Get Spray Areas
Endpoint: GET /api/v1/jobs/:jobId/areas
Returns GeoJSON FeatureCollection of planned spray zones.
Response (200 OK):
{
"type": "FeatureCollection",
"jobId": 12345,
"features": [
{
"type": "Feature",
"properties": {
"name": "North Field",
"type": "area",
"area_ha": 48.5,
"appRate": 50,
"appRateUnit": "lit/ha"
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-74.0060, 40.7128],
[-74.0050, 40.7128],
[-74.0050, 40.7118],
[-74.0060, 40.7118]
]]
}
},
{
"type": "Feature",
"properties": {
"name": "Exclude - Power Lines",
"type": "xcl"
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-74.0055, 40.7125],
[-74.0053, 40.7125],
[-74.0053, 40.7120]
]]
}
}
]
}
Field Meanings:
type: "area"— Planned spray zone (will have appRate/unit)type: "xcl"— Exclusion zone (no-spray boundary, skipped fields)area_ha— Polygon area in hectaresappRateUnit— Material unit string ('lit/ha','oz/ac', etc.)
Import to ArcGIS:
// JavaScript + ArcGIS JS API
const response = await fetch('https://api.agmission.com/api/v1/jobs/12345/areas', {
headers: { 'X-API-Key': apiKey }
});
const featureCollection = await response.json();
const layer = new FeatureLayer({
source: featureCollection.features,
objectIdField: 'OBJECTID',
fields: [...],
renderer: {...}
});
map.add(layer);
4. Trigger Export (Async)
Endpoint: POST /api/v1/jobs/:jobId/export
Initiates async generation of a bulk export.
stateDiagram-v2
[*] --> pending: POST /export returns 202
pending --> processing: async generation starts
processing --> ready: file written to disk
processing --> error: generation failed
ready --> [*]: 24h TTL expires
error --> [*]: TTL expires
Poll GET /exports/:exportId until status: "ready", then call the download endpoint.
Request Body:
{
"format": "csv",
"units": "metric",
"interval": null
}
Parameters:
format(string) —"csv"or"geojson"units(string, optional) —"metric"(default) or"us"interval(number, optional) — GPS point thinning in seconds (float)fm(boolean, optional) —trueto include Flight Master/AgDisp FM fields (sprayHeight_m,driftX_m,driftY_m,depositX_m,depositY_m,radarAlt_m,laserAlt_m). Defaultfalse. Only applicable for customers with FM-enabled equipment.
Response (202 Accepted):
{
"exportId": "66f4a8c1...",
"status": "pending",
"format": "csv",
"units": "metric",
"createdAt": "2026-04-22T14:00:00Z"
}
Status Codes:
202— Export created and queued200— Existing export reused (deduplication — same job/format/units within 5 minutes):{ "exportId": "66f4a8c1...", "status": "ready", "format": "csv", "units": "metric", "createdAt": "2026-04-22T14:00:00Z", "reused": true, "downloadUrl": "/api/v1/exports/66f4a8c1.../download" }429— Rate limit exceeded (checkRetry-Afterheader)409— Invalid parameters
Deduplication: If you POST the same
jobId + format + unitswithin 5 minutes, the server returns the existing export (HTTP 200) instead of creating a new one. Whenreused: trueandstatus: "ready",downloadUrlis included immediately — skip polling.
5. Poll Export Status
Endpoint: GET /api/v1/exports/:exportId
Check generation progress.
Response (200 OK — Pending):
{
"exportId": "66f4a8c1...",
"status": "pending",
"format": "csv",
"units": "metric",
"createdAt": "2026-04-22T14:00:00Z",
"expiresAt": null
}
Response (200 OK — Ready):
{
"exportId": "66f4a8c1...",
"status": "ready",
"format": "csv",
"units": "metric",
"createdAt": "2026-04-22T14:00:00Z",
"expiresAt": "2026-04-23T14:00:00Z",
"downloadUrl": "/api/v1/exports/66f4a8c1.../download"
}
Response (200 OK — Error):
{
"exportId": "66f4a8c1...",
"status": "error",
"error": "Job has no app data to export",
"createdAt": "2026-04-22T14:00:00Z"
}
Polling Best Practice:
import time
import requests
def poll_export(export_id, api_key, max_wait_seconds=600):
start = time.time()
while time.time() - start < max_wait_seconds:
response = requests.get(
f'https://api.agmission.com/api/v1/exports/{export_id}',
headers={'X-API-Key': api_key}
)
data = response.json()
if data['status'] == 'ready':
return data['downloadUrl']
if data['status'] == 'error':
raise Exception(f"Export failed: {data.get('error')}")
# Exponential backoff: 1s, 2s, 4s, ...
time.sleep(min(2 ** (time.time() - start) / 10, 30))
raise TimeoutError('Export generation timeout')
6. Download Export
Endpoint: GET /api/v1/exports/:exportId/download
Stream the ready file.
Response (200 OK):
Content-Type: text/csv (or application/geo+json)
Content-Disposition: attachment; filename="export_job12345_66f4a8c1.csv"
[Binary file stream]
Examples:
# Download as file
curl -X GET "https://api.agmission.com/api/v1/exports/66f4a8c1.../download" \
-H "X-API-Key: ak_test_..." \
-o "export_$(date +%Y%m%d).csv"
# Python with requests
import requests
response = requests.get(
'https://api.agmission.com/api/v1/exports/66f4a8c1.../download',
headers={'X-API-Key': api_key},
stream=True
)
with open('export.csv', 'wb') as f:
for chunk in response.iter_content(8192):
f.write(chunk)
// JavaScript / Node.js
fetch('https://api.agmission.com/api/v1/exports/66f4a8c1.../download', {
headers: { 'X-API-Key': apiKey }
})
.then(r => r.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'export.csv';
a.click();
});
Rate Limiting
See DATA_EXPORT_API_RATE_LIMITING.md for comprehensive rate limit documentation including examples and best practices.
Quick Reference:
| Header | Meaning |
|---|---|
RateLimit-Limit: 20 |
Max requests per account per window |
RateLimit-Remaining: 18 |
Requests left in current window |
RateLimit-Reset: 1745353200 |
Unix timestamp of window reset |
Retry-After: 45 |
Seconds to wait before retrying (on 429) |
Data Formats
CSV Export Columns
All CSV exports include these columns (order may vary by unit system):
Job/Session Metadata (repeated per record):
jobId— Job IDorderNumber— Customer PO numberjobName— Job nameclientId— Client account ID (the applicator's customer this job was done for)clientName— Client account namesessionId— Flight file IDfileName— Log file namepilotName— Pilot name
GPS Data:
timeUtc— ISO 8601 timestamplat— Latitude (decimal degrees)lon— Longitude (decimal degrees)alt_m(metric) /alt_ft(US) — AltitudegrSpeed_ms(metric) /groundSpeed_mph(US) — Ground speed
Application Data:
appRateApplied_Lha(metric) /appRateApplied_galAc(US) — Actual application rateflowRateApplied_Lmin(metric) /flowRateApplied_galMin(US) — Spray system flow rateswathWidth_m(metric) /swathWidth_ft(US) — Boom width
Environment:
windSpeed_kt(metric) /windSpeed_mph(US) — Wind speed (knots / mph)windDir_deg— Wind direction (0-360°)temp_c(metric) /temp_f(US) — Temperaturehumidity_pct— Relative humidity
GeoJSON Export Format
Each point becomes a Feature with Point geometry:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-74.0060, 40.7128, 150.5]
},
"properties": {
"timeUtc": "2026-04-22T09:00:15Z",
"sprayStat": 1,
"grSpeed": 39.8
}
}
Use Cases
Use Case 1: Power BI Incremental Refresh
Goal: Update a Power BI dataset nightly with new GPS records.
Solution:
import requests
from datetime import datetime, timedelta
def sync_to_powerbi(job_id, api_key):
# Get sessions
sessions = requests.get(
f'https://api.agmission.com/api/v1/jobs/{job_id}/sessions',
headers={'X-API-Key': api_key}
).json()
for session in sessions['data']:
file_id = session['sessionId']
# Paginate records
cursor = None
records = []
while True:
params = {'limit': 2000}
if cursor:
params['startingAfter'] = cursor
page = requests.get(
f'https://api.agmission.com/api/v1/jobs/{job_id}/sessions/{file_id}/records',
params=params,
headers={'X-API-Key': api_key}
).json()
records.extend(page['data'])
if not page.get('hasMore'):
break
cursor = page.get('startingAfter')
# Push to Power BI (REST API or XMLA endpoint)
# ...
Use Case 2: ArcGIS Map Automation
Goal: Update ArcGIS Online layer with spray area boundaries.
const job_id = 12345;
const api_key = 'ak_test_...';
// Fetch areas
const areaResponse = await fetch(
`https://api.agmission.com/api/v1/jobs/${job_id}/areas`,
{ headers: { 'X-API-Key': api_key } }
);
const areas = await areaResponse.json();
// Convert to Feature Service format
const features = areas.features.map(feature => ({
geometry: feature.geometry,
attributes: {
name: feature.properties.name,
type: feature.properties.type,
area_ha: feature.properties.area_ha
}
}));
// Add to ArcGIS layer via REST API
const updateResponse = await fetch(
'https://services.arcgis.com/.../updates',
{
method: 'POST',
body: new URLSearchParams({ features: JSON.stringify(features), token: agolToken })
}
);
Use Case 3: Nightly Data Warehouse Load
Goal: Daily batch load all jobs' data into a data lake (S3, Snowflake, etc.).
#!/bin/bash
API_KEY="ak_live_..."
JOBS=(12345 12346 12347)
S3_BUCKET="s3://company-spray-data"
DATE=$(date +%Y%m%d)
for job_id in "${JOBS[@]}"; do
echo "Exporting job $job_id..."
# Trigger export
export_id=$(curl -s -X POST "https://api.agmission.com/api/v1/jobs/${job_id}/export" \
-H "X-API-Key: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{"format":"csv","units":"metric"}' \
| jq -r '.exportId')
# Poll until ready
while true; do
status=$(curl -s -X GET "https://api.agmission.com/api/v1/exports/${export_id}" \
-H "X-API-Key: ${API_KEY}" \
| jq -r '.status')
[ "$status" = "ready" ] && break
sleep 5
done
# Download and upload to S3
curl -s -X GET "https://api.agmission.com/api/v1/exports/${export_id}/download" \
-H "X-API-Key: ${API_KEY}" \
| aws s3 cp - "${S3_BUCKET}/spray_data/job${job_id}/data_${DATE}.csv"
echo "Completed: job $job_id → ${S3_BUCKET}/spray_data/job${job_id}/data_${DATE}.csv"
done
Error Handling
Error Response Format
All errors follow this structure:
{
"error": {
".tag": "error_constant",
"message": "Human-readable details (dev mode only)"
}
}
Common HTTP Status Codes
| Code | Condition | Solution |
|---|---|---|
| 200 | Success | — |
| 202 | Export accepted (async) | Poll /exports/:exportId for completion |
| 400 | Bad request (invalid params) | Check endpoint docs for required fields |
| 401 | Invalid/missing API key | Verify X-API-Key header is present and valid |
| 404 | Resource not found | Check jobId, exportId, fileId exist and belong to your account |
| 409 | Conflict (e.g., invalid format) | Check format is "csv" or "geojson" |
| 429 | Rate limit exceeded | Wait Retry-After seconds, see rate limit docs |
| 500 | Server error | Retry with exponential backoff; contact support if persists |
Example: Handling 429 Rate Limit
import time
import requests
def request_with_backoff(url, api_key, max_retries=3):
for attempt in range(max_retries):
response = requests.get(
url,
headers={'X-API-Key': api_key}
)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
continue
response.raise_for_status()
return response.json()
raise Exception("Max retries exceeded")
Support & SLAs
Support Channels
| Channel | Response Time |
|---|---|
Email: support@agnav.com |
4 hours (business hours) |
| Phone: 1-800-AGNAV-11 | 1 hour (9am-5pm ET) |
| Slack (Enterprise): Dedicated channel | 1 hour |
API SLA
- Availability: 99.5% monthly uptime
- Rate limit quota: 20 requests/min per account (configurable)
- Export timeout: 1 hour max generation time
- File retention: 24 hours after ready
- Data accuracy: ±0.5% for area/volume calculations
Status & Maintenance
- Status Page:
https://status.agmission.com - Maintenance windows: Tuesdays 2-4 AM ET (announced 7 days prior)
- Incident response: PagerDuty escalation, max 15-min response
API Versioning
Current version: v1
- Breaking changes will be announced 90 days in advance
- Deprecation warnings via response headers:
Deprecation: true - Version support policy: At least 3 versions maintained simultaneously
Appendix: Code Examples
cURL Examples
# List sessions
curl -X GET https://api.agmission.com/api/v1/jobs/12345/sessions \
-H "X-API-Key: ak_test_..." \
-H "Accept: application/json"
# Get records with thinning
curl "https://api.agmission.com/api/v1/jobs/12345/sessions/507f1f77.../records?interval=5&limit=1000" \
-H "X-API-Key: ak_test_..."
# Trigger CSV export
curl -X POST https://api.agmission.com/api/v1/jobs/12345/export \
-H "X-API-Key: ak_test_..." \
-H "Content-Type: application/json" \
-d '{"format":"csv","units":"metric"}'
JavaScript / Node.js
const apiKey = 'ak_test_...';
async function fetchSessions(jobId) {
const response = await fetch(`https://api.agmission.com/api/v1/jobs/${jobId}/sessions`, {
headers: { 'X-API-Key': apiKey }
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
}
async function exportAndDownload(jobId) {
// Trigger export
const exportRes = await fetch(`https://api.agmission.com/api/v1/jobs/${jobId}/export`, {
method: 'POST',
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({ format: 'csv', units: 'metric' })
});
const { exportId } = await exportRes.json();
// Poll for ready
let status = 'pending';
while (status !== 'ready') {
const statusRes = await fetch(`https://api.agmission.com/api/v1/exports/${exportId}`, {
headers: { 'X-API-Key': apiKey }
});
({ status } = await statusRes.json());
if (status !== 'ready') await new Promise(r => setTimeout(r, 5000));
}
// Download
return fetch(`https://api.agmission.com/api/v1/exports/${exportId}/download`, {
headers: { 'X-API-Key': apiKey }
});
}
Contact: technical-support@agnav.com
Last Updated: April 22, 2026
Next Review: October 22, 2026