Build on top of Elite Drone School
A REST API for LMS sync, partner integrations, and internal tooling. Read and write student progress, flight logs, missions, and enrollments — all over HTTPS, with bearer-token authentication.
Overview
The API is hosted at https://elitedroneschool.com/api/public/v1. All responses are JSON. Successful responses follow the shape { data, meta? }. Errors follow { error: { code, message } }.
Authentication
Create a key in your API Keys settings. Keys look like edk_live_… and are shown once. Pass them in the Authorization header:
Authorization: Bearer edk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxScopes: read, write, users:read, users:write. write implies read.
Errors & rate limits
Standard HTTP status codes. Common error codes:
401 missing_token / invalid_token— auth failed403 insufficient_scope / forbidden— key lacks scope or workspace access400 invalid_input / invalid_json— request validation failed404 not_found— resource doesn't exist or isn't in your workspace429 rate_limited— 120 req/min per key exceeded500 internal_error / db_error— server-side failure
Every response carries X-RateLimit-Limit and X-RateLimit-Remaining. CORS is open for all origins.
Quickstart
Verify your key with the whoami endpoint:
curl https://elitedroneschool.com/api/public/v1/auth/whoami \
-H "Authorization: Bearer $EDS_API_KEY"{
"data": {
"workspace_id": "5f1c…",
"api_key_id": "9b4a…",
"scopes": ["read", "write"]
}
}Users & enrollments
/v1/usersscope: users:readList all members of your workspace.
Request
curl https://elitedroneschool.com/api/public/v1/users \
-H "Authorization: Bearer $EDS_API_KEY"Response
{
"data": [
{ "id": "uuid", "email": "pilot@example.com", "role": "member", "created_at": "..." }
],
"meta": { "count": 1 }
}/v1/usersscope: users:writeInvite a user by email and add them to your workspace.
Request
curl -X POST https://elitedroneschool.com/api/public/v1/users \
-H "Authorization: Bearer $EDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"new.pilot@example.com","role":"member"}'/v1/users/{id}scope: users:readGet a single member.
Lessons & progress
/v1/lessons/progressscope: readList lesson progress across workspace members. Filter with ?user_id=…&lesson_id=…&limit=…
Request
curl "https://elitedroneschool.com/api/public/v1/lessons/progress?limit=20" \
-H "Authorization: Bearer $EDS_API_KEY"/v1/lessons/progressscope: writeUpsert lesson progress for a workspace member.
Request
curl -X POST https://elitedroneschool.com/api/public/v1/lessons/progress \
-H "Authorization: Bearer $EDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "uuid",
"lesson_id": "part108-module-2",
"completed": true,
"best_time_seconds": 412,
"attempts": 3
}'Flight logs
/v1/flight-logsscope: readList flight logs across workspace members. Filter with ?user_id=…&limit=…
/v1/flight-logsscope: writeIngest a flight log entry.
Request
curl -X POST https://elitedroneschool.com/api/public/v1/flight-logs \
-H "Authorization: Bearer $EDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "uuid",
"duration_seconds": 612,
"distance_km": 3.4,
"flight_date": "2026-06-21T15:30:00Z"
}'Missions
/v1/missionsscope: readList published custom missions. Filter with ?city_id=…&limit=…
/v1/missions/{id}scope: readFetch a single mission, including waypoints.
/v1/missionsscope: writeCreate a custom mission attributed to the API key's owner.
Request
curl -X POST https://elitedroneschool.com/api/public/v1/missions \
-H "Authorization: Bearer $EDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Bridge inspection — Manhattan",
"city_id": "nyc",
"level": "intermediate",
"waypoints": [{"lat":40.71,"lng":-74.00,"alt":40}],
"is_published": true
}'Webhooks
Receive real-time HTTP POSTs when events happen in your workspace. Create endpoints in Settings → Webhooks. Each endpoint gets its own signing secret (whsec_…) used to sign the JSON body.
Event types
flight_log.created— a new flight log entrylesson.progress.updated— any change to lesson progresslesson.completed— lesson transitioned to completed
Payload envelope
POST https://your-app.com/webhooks/eds
Content-Type: application/json
X-EDS-Event: lesson.completed
X-EDS-Event-Id: 6f0e…-uuid
X-EDS-Signature: t=1750000000,v1=<hex_hmac_sha256>
{
"id": "6f0e…-uuid",
"type": "lesson.completed",
"created_at": "2026-06-21T15:30:00.000Z",
"workspace_id": "5f1c…",
"data": {
"id": "row-uuid",
"user_id": "uuid",
"lesson_id": "part108-module-2",
"completed": true,
"best_time_seconds": 412,
"attempts": 3,
"completed_at": "2026-06-21T15:30:00.000Z"
}
}Verifying the signature
The header is t=<unix_ts>,v1=<hex>. Compute HMAC_SHA256(secret, "<t>.<raw_body>") and compare in constant time. Reject requests older than 5 minutes.
// Node.js example
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const [tPart, sigPart] = header.split(",");
const t = tPart.split("=")[1];
const sig = sigPart.split("=")[1];
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(sig, "hex");
const b = Buffer.from(expected, "hex");
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return false;
const age = Math.floor(Date.now() / 1000) - Number(t);
return age >= 0 && age < 300;
}Delivery & retries
Endpoints must respond with a 2xx within 10 seconds. Any non-2xx response, timeout, or network error is automatically retried with exponential backoff:1m → 5m → 15m → 1h → 6h → 24h → 24h (8 attempts total, ±20% jitter). After the final attempt the delivery is marked failed and is visible in your dashboard with the last response code or error. Always make handlers idempotent by deduping on X-EDS-Event-Id — the same event id is re-sent on every retry.