Elite Drone School logoElite Drone School
Developer API · v1

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 } }.

Bearer auth
Long-lived API keys, scoped per workspace.
120 req/min
Per-key rate limit with X-RateLimit headers.
Versioned
Stable URLs under /v1; breaking changes ship under /v2.

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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Scopes: 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 failed
  • 403 insufficient_scope / forbidden — key lacks scope or workspace access
  • 400 invalid_input / invalid_json — request validation failed
  • 404 not_found — resource doesn't exist or isn't in your workspace
  • 429 rate_limited — 120 req/min per key exceeded
  • 500 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

GET/v1/usersscope: users:read

List 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 }
}
POST/v1/usersscope: users:write

Invite 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"}'
GET/v1/users/{id}scope: users:read

Get a single member.

Lessons & progress

GET/v1/lessons/progressscope: read

List 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"
POST/v1/lessons/progressscope: write

Upsert 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

GET/v1/flight-logsscope: read

List flight logs across workspace members. Filter with ?user_id=…&limit=…

POST/v1/flight-logsscope: write

Ingest 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

GET/v1/missionsscope: read

List published custom missions. Filter with ?city_id=…&limit=…

GET/v1/missions/{id}scope: read

Fetch a single mission, including waypoints.

POST/v1/missionsscope: write

Create 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 entry
  • lesson.progress.updated — any change to lesson progress
  • lesson.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.

Need higher rate limits, OAuth, custom retry policies, or webhook IP allowlisting? Email support@elitedroneschool.com.