Developer API v1

Authenticate a reviewed custom bot and read approved CADLY server data.

1 - Overview

The CADLY Developer API lets an approved custom bot read operational data for one Discord server. Access is apply-only. A server owner or Discord administrator must submit the bot and its use case for CADLY staff review before a key can be generated.

Base URL:

https://api.cadly.me/v1

Interactive Swagger reference: https://api.cadly.me/v1/docs

Enter the server API key to unlock the reference. The key is verified against its active grant, loaded into Swagger's Bearer authorization for interactive requests, and kept only in the current tab's memory.

The raw OpenAPI document is available at GET /v1/openapi.yaml with the same Bearer authentication. Documentation validation and specification reads use their own edge protection and do not consume the server grant's normal request quota.

Version 1 is read-only. It does not provide create, update, delete, command, export, archive, or cross-server endpoints.

2 - Applying and managing a key

Open Settings > Danger Zone and use the Developer API card. Members with Manage Settings can view the current application, key status, and rolling 24-hour allowance. Submitting or updating an application and generating, rotating, or revoking a key require the Discord server owner or an administrator.

The application asks for the bot identity and contact, requested data scopes and ER:LC slots, what the bot does, why it needs the data, how it processes and stores responses, its request and rate-limit strategy, hosting, secure deletion, and initial synchronization. You can also provide a source repository. Review the attestations carefully before submitting.

Each approval is bound to:

  • One Discord server
  • One reviewed bot and use case
  • Specific data scopes
  • Approved ER:LC server slots

If reviewers request changes or reject an application, reopen the prefilled form, update the answers, and resubmit it for review.

After approval, generate the key from the same card. The complete key is displayed once; later visits show only a masked identifier and the rolling 24-hour request allowance. Store the complete key in a server-side secret manager or environment variable. Never place it in browser code, a public repository, or Discord messages.

Rotating a key immediately revokes the old key. CADLY does not provide a grace period.

3 - Authentication

Send the key as a bearer token:

Authorization: Bearer cadly_live_<key_id>.<secret>

The key identifies its Discord server. Do not send a guild ID in the URL, query string, or request body.

Available scopes are:

Scope Data

shifts.read Shift history, active shifts, statistics, and leaderboards

logs.read CADLY logs and their public context fields

erlc.read Cached ER:LC server snapshot

loa.read Leave of absence records

sessions.read Session history, details, voters, events, and stored metrics

applications.read Application definitions, aggregate metrics, and submission metadata

4 - Server slot header

ER:LC, session, and shift endpoints are slot-specific.

  • Mono-server CADLY setup: omit X-Server. Sending it returns 400.
  • Dual-server CADLY setup: send X-Server: A or X-Server: B.
  • The requested slot must be included in the grant.

Logs, LOA, and application endpoints are guild-wide and do not use X-Server.

5 - Responses, caching, and errors

Successful responses use a data envelope:

{
  "data": {}
}

Paginated responses also include:

{
  "data": [],
  "pagination": {
    "next_cursor": null,
    "has_more": false
  }
}

Send the returned cursor as ?cursor=.... Do not modify or interpret cursor values.

Eligible GET responses include an ETag. Send it on the next request:

If-None-Match: "<etag>"

If the resource is unchanged, CADLY returns 304 Not Modified with an empty body. A 304 counts toward request limits but does not consume a log or submission record budget.

Errors use a stable code and a request ID:

{
  "error": {
    "code": "invalid_server_header",
    "message": "X-Server must be A or B."
  },
  "request_id": "req_..."
}

Unexpected 5xx responses only return internal_error and Something went wrong. Internal exceptions, database details, and upstream responses are never included. Provide the request ID when contacting CADLY support.

6 - Rate limits

Limits are shared by the server grant. Creating or rotating another key does not provide another quota.

Limit Allowance

Token refill 1 request every 5 seconds

Burst 2 requests

Rolling minute 10 requests

Rolling 24 hours 5,000 requests

Returned CADLY logs 500 per rolling hour

Returned application submissions 500 per rolling hour

Rate information is returned in X-RateLimit-* headers. A rejected request returns 429 and Retry-After.

Repeated reads count again. When an hourly record budget is nearly exhausted, CADLY may return a shorter final page. Once the budget is empty, further reads of that record type return record_budget_exceeded.

7 - Endpoints

7.1 - Shifts

All shift endpoints require shifts.read and the slot rules described above.

Method and path Description

GET /shifts Shift history. Supports user_id, status, shift_type, from, to, cursor, and limit up to 100.

GET /shifts/active Active and on-break shifts.

GET /shifts/{shift_id} One shift.

GET /shifts/members/{discord_id}/stats Shift totals for one Discord member. Supports from and to.

GET /shifts/leaderboard?window=30d Ranked totals for 7d, 30d, 90d, or all.

7.2 - Logs

Method and path Description

GET /logs Logs with type, target_id, created_by, status, from, to, cursor, and limit up to 50.

GET /logs/{log_id} One log.

The context object contains only documented public fields. Internal metadata and import-provider identifiers are omitted.

7.3 - ER:LC

GET /erlc requires erlc.read and returns CADLY's existing cached snapshot. It never wakes a sleeping server or requests a refresh.

The snapshot contains:

  • Server name, OwnerId, CoOwnerIds, player capacity, JoinKey, account verification setting, and team balance
  • Players and their team, callsign, location, permission, and wanted stars
  • Staff groups
  • Join, kill, command, moderator-call, and emergency-call data
  • Queue and vehicles
  • cached_at and stale cache metadata

JoinKey is the player-visible in-game join code. It is not an API credential. CADLY's ER:LC API credential is never returned.

When no cached snapshot exists, the endpoint returns a generic 503 response.

7.4 - Leave of absence

Method and path Description

GET /loa LOA history with user_id, status, from, to, cursor, and limit up to 50.

GET /loa/active Active LOAs ordered by end time.

GET /loa/{loa_id} One complete LOA record.

7.5 - Sessions

All session endpoints require sessions.read and the slot rules described above.

Method and path Description

GET /sessions/current Current state, timing, cached players and in-game staff, voters, and recent lifecycle events.

GET /sessions/history Session history with cursor pagination and a limit up to 50.

GET /sessions/{session_id} Stored lifecycle information, configuration snapshot, voters, and events.

GET /sessions/{session_id}/metrics Stored player, staff, queue, command, kill, and average-playtime samples. Limit up to 250.

No additional telemetry is collected for this API.

7.6 - Applications

Only these application endpoints are available:

Method and path Description

GET /applications Application ID, public ID, name, description, open state, URL, and timestamps.

GET /applications/{application_id}/metrics?window=30d Counts for 7d, 30d, 90d, or all.

GET /applications/{application_id}/submissions Submission identity, released status, and timestamps. Limit up to 25.

Submission answers, review notes, staged decisions, AI data, input telemetry, and internal settings are not returned.

8 - Example

curl https://api.cadly.me/v1/sessions/current \
  -H "Authorization: Bearer $CADLY_API_KEY" \
  -H "X-Server: A"

For a mono-server setup, remove the X-Server header.

Browse all CADLY documentation