SlateFire developer docs
Everything you need to integrate SlateFire into your Godot 4 project — from the GDScript SDK to the raw HTTP API. Every method, parameter, and response shape documented below comes from the actual addons/slatefire/ source.
Install & setup
- Install the plugin. Drop the
addons/slatefire/directory into your Godot project, or install from the Asset Library. - Enable it. Go to Project → Project Settings → Plugins and toggle SlateFire on. The plugin registers a singleton named
SlateFire(extends Node) that is available everywhere in GDScript.
Configure
Call SlateFire.configure() once, typically in your main scene's _ready(). You need a publishable API key from the SlateFire dashboard.
pk_live_xxxxxxxx). Must not be empty.
opts.base_urlStringBase URL for API requests. Defaults to https://slatefire-guard.john-malmin.workers.dev/v1. Set this to https://api.slatefire.dev/v1 when the custom domain is live.
opts.auto_retrybool (default true)Auto-back-off and retry on rate_limited responses. Retries up to max_retries times.
opts.max_retriesint (default 3)Maximum retry attempts when auto-retry is active.
opts.request_timeout_sfloat (default 10.0)HTTP request timeout in seconds.
When configure() is called, the SDK automatically:
- Creates all sub-modules (
auth,leaderboards,saves,analytics,config) - Fetches remote config (returns local cache — no backend endpoint yet)
- Sets
saves.max_bytesfrom thesave_max_bytesconfig field (default 245,760 bytes / 240 KB)
set_player_token
Write endpoints (submit score, save data, delete saves) require a player token. Set it after your player authenticates. The SDK does not issue player tokens itself — tokens must be handed to the game client from your own trusted system (or hand-minted for testing).
x-player-token header on write requests.
auth.sign_in_anonymous() is fully deployed — it creates a player, issues an HMAC-signed token (30-day expiry), sets it automatically, and emits signed_in. Other auth methods (register_email, sign_in_email, etc.) are still not_implemented.
Make your first call
Here is the full getting-started flow — sign in anonymously, then submit a score and read the leaderboard. All three calls work against the live backend.
All working sign_in_anonymous, leaderboards.submit, leaderboards.top, saves.put, saves.get, and saves.delete all work against the live backend.
SDK Reference
Every public member of the SlateFire singleton and its sub-modules. Methods are tagged by implementation status against the live backend.
SlateFire (singleton)
The autoload registered by the plugin. Extends Node. Created automatically when the plugin is enabled — no manual instantiation.
Properties
| Property | Type | Description |
|---|---|---|
auth | SlateFireAuth | Player authentication sub-module. |
leaderboards | SlateFireLeaderboards | Leaderboard read/write sub-module. |
saves | SlateFireSaves | Cloud save sub-module. |
analytics | SlateFireAnalytics | Analytics event tracking sub-module. |
config | SlateFireConfig | Remote config sub-module (local-only for now). |
is_online | bool | Read-only. false when the SDK detects a connectivity failure. |
is_paused | bool | Read-only. true while writes are blocked due to a service_paused response. |
configure
Initializes the SDK. Creates sub-modules, fetches config, sets saves.max_bytes. Must be called before any other SDK method. The api_key must not be empty.
Options (all optional):
| Key | Type | Default | Description |
|---|---|---|---|
auto_retry | bool | true | Auto-backoff and retry on rate_limited responses for write operations. |
max_retries | int | 3 | Maximum retry attempts. |
request_timeout_s | float | 10.0 | HTTP request timeout in seconds. |
set_player_token
Sets the HMAC-signed player token sent as x-player-token on write requests. Also notifies SlateFire.auth via _on_token_changed().
Signals
| Signal | Arguments | Fired when |
|---|---|---|
service_paused | retry_after: int | Backend returns HTTP 503. Writes are blocked for retry_after seconds. |
service_resumed | — | Pause period expires. Writes may resume. |
request_failed | error: SlateFireError | Any error from an SDK network call. |
quota_warning | percent: int | Local usage estimate crosses 80% or 95% (not currently emitted by the SDK — reserved for future). |
quota_reached | kind: String | Backend returns HTTP 402 with mau_quota or write_quota. |
SlateFireResponse
Every await-able SDK call returns one of these.
| Property | Type | Description |
|---|---|---|
ok | bool | true on success, false on error. |
data | Variant | The response payload. Type depends on the call (Dictionary, Array, String, bool, null). null on error. |
error | SlateFireError | null when ok == true, otherwise a SlateFireError instance. |
SlateFireError
| Property | Type | Description |
|---|---|---|
code | String | Stable error code (see error reference). |
message | String | Human-readable message. |
status | int | HTTP status code; 0 for offline/timeout errors. |
retry_after | int | Seconds to wait before retrying. 0 when not applicable. |
SlateFire.auth Working
Player authentication. All sign-in methods call their respective backend endpoints and automatically set the returned player token on the HTTP client. Email registration uses PBKDF2 password hashing server-side. Provider sign-in creates a new player on first use and links the provider for future sign-ins.
Properties
| Property | Type | Description |
|---|---|---|
current_player | Dictionary | Currently signed-in player data. Empty dict when signed out. |
is_signed_in | bool | false until a sign-in method succeeds. |
Signals
| Signal | Arguments |
|---|---|
signed_in | player: Dictionary |
signed_out | — |
Methods
POSTs to /v1/players/anonymous. On success, automatically sets the returned player token via set_player_token(), populates current_player, sets is_signed_in = true, and emits signed_in.
Creates a new player account with email and password. Password is hashed server-side with PBKDF2. Returns the standard player object with a signed token. Returns email_taken (409) if the email is already registered for this project.
Errors: email_and_password_required (400), email_taken (409).
Signs in with email and password. Verifies the password hash server-side. Returns the standard player object with a signed token. Returns invalid_credentials (401) if the email or password is wrong.
Errors: email_and_password_required (400), invalid_credentials (401).
Signs in with an external provider (e.g. "steam", "google", "discord", "apple"). The token is the provider's user ID for this player. On first use, a new player is created and the provider link is stored. On subsequent calls, the existing player is returned. The player token is automatically set.
Errors: provider_and_token_required (400).
Links an email/password to the currently signed-in player. Requires an active player token (call sign_in_anonymous or another sign-in method first). Returns email_taken (409) if the email is already in use.
Errors: unauthorized (401, if not signed in), email_and_password_required (400), email_taken (409).
Links an external provider to the currently signed-in player. Requires an active player token. Returns provider_already_linked (409) if that provider account is already linked to a player.
Errors: unauthorized (401), provider_and_token_required (400), provider_already_linked (409).
Clears current_player, sets is_signed_in = false, and emits signed_out. Works locally only — no network call.
Updates the current player's profile. Accepted fields: display_name (String), avatar_url (String). Requires an active player token. Sends POST /v1/players/update with the traits as the JSON body.
Errors: unauthorized (401).
SlateFire.leaderboards
Submit and read leaderboard scores. Boards are created in the developer dashboard. Methods below are tagged with their backend status.
Submits a score for the current player. The server keeps the best score per player (uses ON CONFLICT ... DO UPDATE SET score = MAX(score, excluded.score)).
| Parameter | Type | Description |
|---|---|---|
board_id | String | The leaderboard ID (created in the dashboard). |
score | int | The score to submit. |
metadata | Dictionary | Optional metadata (currently ignored by the backend — stored and returned as empty {} in the response). |
Success: data is a Dictionary with rank, player_id, display_name, score, metadata, is_current_player.
Errors: invalid_api_key, unauthorized, rate_limited, mau_quota, write_quota, service_paused, score_required (400 if score is missing or not a number).
Returns the top entries for a leaderboard. The count parameter is accepted by the SDK but the backend hard-codes a LIMIT 100 and ignores the count — you always get up to 100 entries regardless of what you pass.
Success: data is an Array of Dictionaries with rank, player_id, display_name, score, metadata, is_current_player.
Errors: invalid_api_key, not_found.
Returns up to 2*radius + 1 entries centered around the current player's rank. Requires a signed x-player-token. The player must have a score on the board.
Success: data is an Array of Dictionaries with rank, player_id, display_name, score, metadata, is_current_player.
Errors: invalid_api_key, unauthorized, not_found (if the player hasn't submitted a score on this board).
Returns the current player's score and rank on a leaderboard. Requires a signed x-player-token.
Success: data is a Dictionary with rank, player_id, display_name, score, metadata, is_current_player.
Errors: invalid_api_key, unauthorized, not_found (if the player hasn't submitted a score on this board).
Returns scores for the current player's friends on a leaderboard. Friends are managed with add_friend() and remove_friend(). Requires a signed x-player-token.
Success: data is an Array of Dictionaries with rank, player_id, display_name, score, metadata, is_current_player.
Errors: invalid_api_key, unauthorized.
Adds a friend relationship. The friend_id is the other player's UUID. Requires an active player token.
Errors: unauthorized, friend_id_required (400).
Removes a friend relationship. Requires an active player token.
Errors: unauthorized, friend_id_required (400).
SlateFire.saves
Per-player key/value JSON blobs. The SDK caches reads to user://slatefire/saves/ for offline access.
| Property | Type | Description |
|---|---|---|
max_bytes | int | Per-slot size limit for the current plan (default 245760 / 240 KB). Set automatically from config on configure(). |
Writes a JSON save slot. The SDK serializes the Dictionary to JSON, checks the byte size against max_bytes before uploading, and returns save_too_large immediately if it exceeds the limit. The backend tracks version and updated_at via R2 custom metadata. Pass expected_version for optimistic concurrency (returns 409 on mismatch).
| Parameter | Type | Description |
|---|---|---|
key | String | Save slot identifier (e.g. "profile", "level_7_state"). |
data | Dictionary | JSON-serializable save data. |
expected_version | int (default -1) | Optimistic concurrency version. Sends as ?expected_version=N query param. Returns version_conflict (409) if the stored version doesn't match. |
Success: data is a Dictionary with key, data, version, updated_at.
Errors: save_too_large (413, checked client-side before request), version_conflict (409), invalid_api_key, unauthorized, rate_limited, service_paused.
Reads a save slot. The backend returns a JSON envelope with the parsed data, version, and updated_at. On success, the SDK caches the result to user://slatefire/saves/{key}.json. On failure, it falls back to the cached copy if available.
Success: data is a Dictionary with key, data (parsed JSON Dictionary), version, updated_at.
Errors: invalid_api_key, unauthorized, not_found (404).
Returns an Array of save key strings for the current player. Calls GET /v1/saves, which lists all keys under the player's R2 prefix.
Errors: invalid_api_key, unauthorized.
Deletes a save slot. Also removes the local cache. On success, data is true.
Errors: invalid_api_key, unauthorized, not_found.
SlateFire.analytics Working
Fire-and-forget event tracking. Events queue locally and are flushed to the backend in batches on a 5-second debounce timer. The backend stores events in D1 with the player's ID.
Appends an event to the local queue with the current timestamp. The queue is persisted to user://slatefire/analytics/queue.json. A 5-second debounce timer triggers a batch flush via POST /v1/analytics/batch. Flushed events are cleared from the queue on success.
Sends a POST /v1/analytics/identify call to associate traits with the current player. Stubbed to acknowledge the request; player identification can also be managed via auth.update_player().
SlateFire.config Working
Remote feature flags and game balance values. config.fetch() calls GET /v1/config which returns the project's stored config. Falls back to the local cache if the backend is unreachable. Set config values in the dashboard.
Signals
| Signal | Arguments | |
|---|---|---|
updated | — | Emitted after each fetch() call. |
Returns a copy of the cached flags. data is a Dictionary.
Each getter checks the cached flags. If the key is missing or the type doesn't match, the default is returned.
HTTP API Reference
For developers integrating without the GDScript SDK, or who want to understand the wire protocol. All endpoints are under the base URL https://slatefire-guard.john-malmin.workers.dev/v1.
Authentication
Every request requires x-api-key (the publishable key). Write operations (POST, PUT, DELETE) also require x-player-token (a signed HMAC token identifying the player).
CORS is open (access-control-allow-origin: *) so browsers can call the API directly.
token as the x-player-token header on all subsequent write requests.x-player-token: signed player token
Content-Type: application/json
The server uses INSERT ... ON CONFLICT DO UPDATE SET score = MAX(score, excluded.score) — only best scores are kept.
count parameter is not supported.x-player-token: signed player token
Returns 404 if the player hasn't submitted a score on this board yet.
2*radius + 1 entries centered around the requesting player's rank.x-player-token: signed player token
Returns 404 if the player hasn't submitted a score on this board yet. The radius defaults to 5 and is capped at 100.
x-player-token: signed player token
Content-Type: application/octet-stream
Server-internal key format: {projectId}/{playerId}/{key}. The SDK handles this transparently.
application/octet-stream. No metadata envelope. Requires the player token so saves are properly scoped per-player.x-player-token: signed player token
Returns 404 if the slot doesn't exist. Server-internal key format: {projectId}/{playerId}/{key} — same as the write path, so reads and writes address the same slot.
x-player-token: signed player token
Error codes
The backend returns error responses as JSON with an error field. The SDK maps these to SlateFireError.code values.
| error.code | HTTP status | Meaning |
|---|---|---|
invalid_api_key | 401 | The x-api-key is missing or doesn't match any project. |
unauthorized | 401 | Write request without a valid x-player-token. |
rate_limited | 429 | Too many writes too fast. Includes retry-after header. The SDK auto-retries by default. |
mau_quota | 402 | Monthly active-player cap reached for the project's plan. |
write_quota | 402 | Monthly write cap reached for the project's plan. |
save_too_large | 413 | Save data exceeds the plan's per-slot limit or the absolute 8 MB ceiling. The SDK also checks this client-side before uploading. |
body_too_large | 413 | Request body exceeds the absolute 8 MB limit (enforced before any plan check). |
service_paused | 503 | Backend is temporarily read-only (panic flag active for free-tier projects). Includes retry-after. The SDK caches this and blocks writes without network calls. |
not_found | 404 | The requested resource (save slot, leaderboard) doesn't exist. |
score_required | 400 | Leaderboard submit request body is missing or has a non-numeric score. |
conflict | 409 | Defined in the SDK for future save version conflicts; not yet returned by the backend. |
offline | 0 | Network error / no connectivity. SDK-only error code. |
timeout | 0 | Request exceeded request_timeout_s. SDK-only error code. |
not_implemented | 0 | Returned by SDK methods whose backend routes don't exist yet. |
Concepts
API key vs Player token
SlateFire uses a two-key authentication model:
| Publishable API key | Player token | |
|---|---|---|
| What it is | A project-level key from the dashboard (e.g. pk_live_xxxxxxxx) | An HMAC-SHA256 signed token identifying the current player |
| Where it goes | x-api-key header (every request) | x-player-token header (write requests only) |
| Required for | All reads and writes | Writes only (POST, PUT, DELETE) |
| Set via | SlateFire.configure("pk_...") | SlateFire.set_player_token("...") |
| Who issues it | SlateFire dashboard | auth.sign_in_anonymous() issues one automatically. You can also call set_player_token() with a hand-minted token for testing. |
Why two keys? The API key identifies the project. The player token identifies who is acting. Reads (leaderboard top, save load) need only the project key. Writes (score submit, save put) need both, so the server can enforce per-player rate limits, MAU counting, and panic gating.
Plan limits
Every project has a plan that governs resource limits. The backend enforces them server-side — these are walls, not suggestions.
| Limit | Hobby (free) | Indie ($19/mo) | Studio ($99/mo) |
|---|---|---|---|
| MAU / month | 5,000 | 50,000 | 250,000 |
| Writes / month | 2,000,000 | 50,000,000 | 500,000,000 |
| Max save size | 256 KB | 1 MB | 4 MB |
| Write rate (per-player) | 5 / sec, burst 20 | 20 / sec, burst 60 | 50 / sec, burst 150 |
| Panic bypass | No (free projects go read-only during a panic event) | Yes | Yes |
There is also a hard global ceiling of 8 MB per request body, enforced before any plan check. Beyond that, a global monthly write budget of 20 million acts as a circuit breaker — if crossed, the panic flag is set automatically and free-tier projects are blocked from writing (the cron backstop auto-recovers when the count drops below 50% of budget).