v2.4.1 — stable

ElkScale API Documentation

Reference documentation for the ElkScale REST API. Manage session bookings, query node availability, and monitor pellet inventory.

Getting Started

The ElkScale API allows you to programmatically manage session bookings, query node availability, and monitor pellet vending machine inventory. Requests are authenticated via API key. The API is hosted on AWS. The elk are not on AWS.

Base URL

Base URL https://api.elkscale.io/v2

All requests should be made over HTTPS. HTTP requests will receive a redirect. Gary requests that you don't do that; the redirect logic was written by a contractor in 2023 and nobody has looked at it since.

Timezone note: All timestamps in the API are returned in UTC. Gary operates on local time. These are sometimes different by up to an hour depending on whether daylight saving has occurred recently and whether Gary has updated his phone.

Request Format

All request and response bodies are JSON. Set Content-Type: application/json on requests with a body. Empty responses return 204 No Content. Malformed requests return 400 and Gary is notified by email.


Authentication

All API requests require authentication via an API key, passed in the request header.

X-ElkScale-Key: esk_live_xxxxxxxxxxxxxxxxxxxx

Your API key is included in your welcome email. It begins with esk_live_ followed by a 20-character alphanumeric string. Please treat this as a secret. Do not commit it to GitHub. Devon from Stratum Labs did this in November and Gary got seventeen unexpected booking requests from a Danish IP address before anyone noticed.

Key delivery: If Gary forgot to include your API key in the welcome email, reply to the welcome email. If Gary has not opened the welcome email yet, it will have a small paperclip icon in his inbox. He is aware of this. See the Contact page if this persists beyond 48 hours.

Key Rotation

API keys do not expire automatically. To rotate your key, contact Gary. Gary will generate a new key and email it to you. The old key remains active for 72 hours during the transition period, or until Gary manually invalidates it, whichever comes first.


Endpoints — Sessions

Sessions represent a booked interaction window at the ElkScale facility. All sessions are 45 minutes in duration. This is not configurable.

GET /sessions

Returns a list of upcoming sessions associated with your account. Completed sessions are retained for 90 days. Gary's personal notes on sessions are included where available in the notes field.

Response 200{
  "sessions": [
    {
      "id": "sess_a7f2c91b",
      "scheduled_at": "2026-04-08T10:00:00Z",
      "duration_minutes": 45,
      "group_size": 8,
      "tier": "growth",
      "node_density_estimate": "moderate",
      "status": "confirmed",
      "notes": "Gary notes Kevin is in a mood. Recommend avoiding the east paddock."
    },
    {
      "id": "sess_b3d8e42a",
      "scheduled_at": "2026-04-22T14:00:00Z",
      "duration_minutes": 45,
      "group_size": 12,
      "tier": "growth",
      "node_density_estimate": "high",
      "status": "confirmed",
      "notes": "Gerald was spotted near the gate this morning. Good sign."
    }
  ],
  "total": 2
}
POST /sessions

Books a new session. The request will be logged in ElkScale's system and forwarded to Gary for manual confirmation. Sessions are not considered confirmed until Gary has reviewed and accepted the booking.

Request body{
  "date": "2026-05-14",
  "preferred_time": "10:00",
  "group_size": 6,
  "tier": "growth",
  "special_requirements": "One attendee has a mild fear of large animals. Please advise."
}
Response 201{
  "id": "sess_c9f1d77e",
  "status": "pending_gary",
  "scheduled_at": null,
  "message": "Your session request has been received. Gary will confirm via calendar invite within 48 hours.",
  "pellet_allocation_kg": 1.0
}
Manual confirmation: Bookings are confirmed by Gary manually. You will receive a calendar invite from gary@[redacted]. This may take up to 48 hours. Gary does not work weekends. If you submit a booking request on a Friday afternoon, please expect confirmation on Monday. Gary has asked us to make this very clear.
DELETE /sessions/{id}

Cancels a confirmed session. A successful cancellation returns 204 No Content.

Cancellation policy: Cancellations received within 24 hours of the scheduled session start time forfeit your pellet allocation. The pellets will be given to the elk regardless. They are not refundable. The elk do not accept returns.

Endpoints — Nodes

Nodes are the organic compute units that form the ElkScale infrastructure. They are elk. You cannot provision, configure, or deprovision them. You can query their current status and last known location.

GET /nodes

Returns the current node roster. The roster is updated by Gary when he does his morning rounds. The "the_others" entry is a placeholder; Gary knows their names but has not entered them into the system yet. He has been meaning to.

Response 200{
  "nodes": [
    {
      "id": "node_gerald",
      "name": "Gerald",
      "antler_class": 12,
      "weight_kg": 381,
      "interaction_mode": "observational_then_approach",
      "current_location": "east_treeline",
      "last_seen": "2026-03-25T08:14:00Z"
    },
    {
      "id": "node_brenda",
      "name": "Brenda",
      "antler_class": null,
      "weight_kg": 278,
      "interaction_mode": "high_initiative",
      "current_location": "main_paddock",
      "last_seen": "2026-03-25T09:02:00Z"
    },
    {
      "id": "node_kevin",
      "name": "Kevin",
      "antler_class": 6,
      "weight_kg": 312,
      "interaction_mode": "variable",
      "current_location": "unknown",
      "last_seen": "2026-03-24T16:45:00Z"
    },
    {
      "id": "node_others",
      "name": "The Others",
      "count": 7,
      "antler_class": "mixed",
      "interaction_mode": "herd_dependent",
      "current_location": "somewhere in the back",
      "last_seen": "2026-03-25T07:50:00Z"
    }
  ]
}
GET /nodes/{id}

Returns the full record for a specific node. The example below shows Gerald's canonical record.

GET /nodes/node_gerald — Response 200{
  "id": "node_gerald",
  "name": "Gerald",
  "species": "Cervus canadensis",
  "antler_class": 12,
  "weight_kg": 381,
  "age_years": 9,
  "interaction_mode": "observational_then_approach",
  "pellet_preference": "standard_mix",
  "current_location": "east_treeline",
  "last_seen": "2026-03-25T08:14:00Z",
  "availability": "not_guaranteed",
  "notes": "Gerald will approach on his own schedule. Do not rush Gerald."
}
Location accuracy: Node location data is updated manually by Gary when he spots them on his morning rounds. Accuracy: approximately. Kevin's current_location has read "unknown" since November 2025. Kevin is definitely still there. Gary sees him most days. He just tends to wander.
DELETE /nodes/{id}

Attempts to deprovision a node.

Response 403 Forbidden{
  "error": "Nodes cannot be deprovisioned. They are elk.",
  "code": "NODE_IS_ELK",
  "documentation_url": "https://api.elkscale.io/v2/docs#nodes"
}
This endpoint will always return 403. It exists because someone asked Gary if they could deprovision a node and Gary said "I suppose I could add that endpoint" without fully considering the implications. You cannot deprovision a node. Please do not ask Gary about this.

Endpoints — Pellets

The pellet subsystem provides read-only access to the vending machine inventory status. Pellet purchases cannot be made via the API. There is a vending machine near the main gate. It takes coins. A sign on the machine says it also takes cards but Gary says the card reader has not worked since 2024 and he has emailed about it twice.

GET /pellets/inventory

Returns current vending machine stock and operational status. Gary restocks on Mondays after crossfit.

Response 200{
  "stock_kg": 4.2,
  "capacity_kg": 8.0,
  "last_restocked": "2026-03-24T10:30:00Z",
  "price_per_handful_usd": 1.00,
  "accepts_coins": true,
  "accepts_cards": false,
  "accepts_cards_according_to_sign": true,
  "machine_status": "operational",
  "known_issues": [
    "Canadian quarter lodged in mechanism since 2025-11-14",
    "Dispenser lever is sticky — pull firmly but not aggressively"
  ]
}

Rate Limits

Rate limits are applied per API key on a rolling hourly window. If you exceed your limit, requests will return 429 Too Many Requests with a Retry-After header indicating when the window resets.

Tier Requests / hour Notes
starter 50 Sufficient for session management at normal human scheduling velocity.
growth 100 Standard allocation. Devon from Stratum Labs has never exceeded this.
enterprise 500 Nuvora Systems uses approximately 12 requests per month.
The elk do not have rate limits. Gary informally rate-limits himself to 3 emails per week, though he has been known to send a fourth if there is something important about the pellet machine or if Kevin has done something notable.

Error Codes

The ElkScale API uses standard HTTP status codes. All error responses include a JSON body with an error string and, where applicable, a code field for programmatic handling.

Code Status Description
200 OK Request successful. Gary has seen it.
201 Created Session booked. Gary will send a calendar invite. Please allow up to 48 hours.
400 Bad Request Your request was malformed or missing required fields. Gary is confused. Check the request body.
401 Unauthorized Invalid or missing API key. Check your X-ElkScale-Key header. See Gary if the issue persists.
403 Forbidden You tried to DELETE a node. Stop that. Nodes are elk. See Endpoints — Nodes.
404 Not Found The requested resource does not exist, or Kevin has wandered off again.
429 Too Many Requests You have exceeded your rate limit. Slow down. You are also possibly emailing Gary too much. Please write a letter instead.
500 Internal Server Error Something has gone wrong on our end. Specifically, the Raspberry Pi that runs the booking service has frozen. Gary is rebooting it.
503 Service Unavailable Gary is at crossfit. The Raspberry Pi requires a manual restart and Gary is the only one who knows which plug it is. Try again after 09:30 on a Monday.

Error Response Format

{
  "error": "A human-readable description of what went wrong.",
  "code": "MACHINE_READABLE_CODE",
  "request_id": "req_8c3f2b1a",
  "gary_notified": true
}

SDKs

Official SDKs

None. Gary does not know what an SDK is. When the concept was explained to him he said "so it's like a library for your API?" and then nodded and did not follow up. We are not ruling out an official SDK in a future release. Gary is supportive of the idea in principle.

Community SDKs

Also none. We have 47 customers. Three of them are developers. One of them — Devon at Stratum Labs — has threatened to write a Python wrapper. He posted a GitHub issue titled elkscale-py: unofficial client library (WIP) in February and has not committed anything yet. We are supportive of this and have told Devon so. Gary asked if Devon would put ElkScale's name in the README. Devon said yes. Gary has not followed up since but we believe he is pleased.

In the meantime: The API is simple enough to use with curl, any HTTP client library, or Postman. If you have questions about integration, contact Gary. Gary will forward your email to his nephew, who Gary describes as "good with computers."