Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

horizOn

NPM · HORIZON-MCP

Docs, live API tools and prompts for the horizOn game backend in Godot, Unity and Unreal.

Available components

+4 this week 89 Trust /100

Last scanned (19 hours ago)

Scores higher than 91% of npm servers

Category Entertainment and Games

Trust breakdown (7 categories)

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →

Supply Chain Security98
  • No malware found by supply-chain analysis.Pass
  • No known CVEs affecting this package version or its production dependencies.Pass
  • No install/post-install scripts declared.Pass
  • 31 of 93 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency97
  • Source repository is publicly reachable at the declared URL. View diagnostics → Pass
  • Cryptographically verified build provenance (signed, bound to ProjectMakersDE/horizOn-mcp). View diagnostics → Pass
  • Clear OSI-approved license (MIT).Pass
  • Actively maintained (last published 5 days ago).Pass
  • Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability79
  • 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
  • AI-judged instruction clarity (excellent).Pass
  • Context-footprint check failed: tool/resource definitions use about 13461 tokens (~258/item across 52 items; 34 tools + 18 resources), over budget; trim descriptions and params. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management58
  • Stability check failed: the tool surface changed between 1.5.5 and 1.8.1: 0 tool removals, 1 breaking changes, 6 additions. See how to fix → Fail
Tool Coverage100
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 100% of tool parameters carry a description.Pass
  • Structured output schemas are declared (47% of tools); any adoption earns full credit.Pass
Tool Safety100
  • No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
  • All 2 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
  • An AI judge read all 35 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
  • Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 15 Oct 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 52 to 55.

  • 14 Oct 26 +39
    • Stability: unverified → fail ▼ security
    • Injection markers: unverified → pass ▲ security
    • Security disclosure: fail → unverified ▼ functional
    • Tool coverage: unverified → 100 ▲ functional
    • Schema quality: unverified → 100 ▲ functional
    • MCP protocol: unverified → pass ▲ functional
    • Package version: 1.7.0 → 1.8.1 functional
  • 12 Oct 26 −36
    • Stability: fail → unverified ▼ security
    • Tool safety: pass → unverified ▼ security
    • Capabilities: pass → unverified ▼ functional
    • Tool coverage: 100 → unverified ▼ functional
    • Schema quality: 100 → unverified ▼ functional
    • Package version: 1.7.0 → 1.8.1 functional
  • 7 Oct 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 18 to 28.

  • 4 Oct 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 15 to 18.

  • 3 Oct 26 +15
    • Malware scan: unverified → pass ▲ security
  • 2 Oct 26 +1
    • Malware scan: unverified → pass ▲ security
    • Package version: 1.6.0 → 1.7.0 functional
  • 1 Oct 26 −15
    • Stability: unverified → fail ▼ security
    • Malware scan: pass → unverified ▼ security
    • Schema quality: 129 → 247 ▼ functional
    • First check of Tool coverage: 47 functional
    • Package version: 1.5.5 → 1.6.0 functional
Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 16 Oct 2026 · Analysed npm/horizon-mcp@1.8.1

Provenance Verified

A signed build attestation was found and verified, binding this exact artifact to the source repository it claims to come from.

Result Verified
Ecosystem npm
Reason Verified
Discovered via Registry attestation endpoint
Source repo ProjectMakersDE/horizOn-mcp
Certificate issuer https://token.actions.githubusercontent.com
Certificate SAN https://github.com/ProjectMakersDE/horizOn-mcp/.github/workflows/release.yml@refs/heads/release
Rekor log index 3193087463
Predicate type SLSA build provenance https://slsa.dev/provenance/v1
Subject digest sha512:e6b20ebb6b68af3e29e3ca0160e519f928718941b922fffa2f71733e93c6b92684c89ec774712ff5f3bd8d2f3b7a72994cd8ff94042b954f7b88b4012

Background: How many MCP packages publish verified provenance →

Dependencies 93 packages
Packages resolved 93
Stale 31
Tree resolution Complete

Background: SBOMs and build attestations, explained →

MCP tools · 34 exposed · ~12,938 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →

Tool Tokens
horizon_cancel_email ~242

Cancels a queued email that is still pending (sent now or scheduled) so it is never delivered. Use when: a scheduled email is no longer wanted, for example the player already came back. Not for: emails that are already processing or sent (they cannot be recalled); check with horizon_get_email_status first when unsure. Requires: the emailId from horizon_send_email, sent with the same API key. Side effects: the email is never sent and cannot be restored; to send it later, call horizon_send_email again. Returns: {message: 'Email cancelled'}. Errors: An email that is not pending (already processing, sent, failed or cancelled) or belongs to another API key is rejected: read its state with horizon_get_email_status. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
emailIdstringyesEmail ID (UUID) returned by horizon_send_email

No output schema declared.

No examples provided.

horizon_check_auth ~306

Checks whether a player's session token (accessToken) is still valid for the given user. Use when: before a session tool when the token may have expired, or when a game resumes, to decide whether the player must sign in again. Not for: testing the API key or base URL (use horizon_test_connection) or getting a new token (use horizon_signin_email or horizon_signin_anonymous). Requires: userId and the accessToken from horizon_signin_email or horizon_signin_anonymous. Side effects: a valid check refreshes the session's one hour idle timeout; nothing else changes. Returns: {userId, isAuthenticated, authStatus, message}. An invalid or expired token is not an HTTP error: isAuthenticated is false, so sign in again with horizon_signin_*. Errors: 400 for a malformed userId or token: pass the values from the sign-in result. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous (max 256 characters)
userIdstringyesPlayer user ID (UUID) from the result of horizon_signup_* or horizon_signin_*
NameTypeReqDescription
authStatus–––
isAuthenticated–––
message–––
userId–––

No examples provided.

horizon_create_crash_report ~598

Submits one crash (CRASH), caught exception (NON_FATAL) or freeze (ANR) with device data to horizOn Crash Reporting, grouped by fingerprint. Use when: the game crashed (send on next start) or caught an exception worth tracking. Not for: technical events without a crash (use horizon_create_log) and messages written by players (use horizon_submit_feedback). Requires: a sessionId registered with horizon_create_crash_session for this app run; userId is optional. Side effects: adds one report per call (a repeat adds a duplicate) and marks the session as crashed. Reports with the same fingerprint form one group; a group marked resolved becomes REGRESSED when a newer app version crashes again. Returns: {id, groupId, createdAt}; groupId identifies the crash group in the dashboard. Errors: 400 when a required field is missing or too long: fix it and resend. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
appVersionstringyesApp version (e.g. '1.2.3')
breadcrumbsarray–Recent events before the crash, oldest first (max 50)
customKeysobject–String key to string value metadata such as level or build flavor (max 10 entries)
deviceMemoryMbinteger–Device RAM in MB, for example 8192
deviceModelstringyesDevice model (e.g. 'Pixel 8', 'iPhone 15')
fingerprintstringyesGrouping key: reports with the same fingerprint form one crash group, for example a hash of exception type and top stack frame (1 to 128 characters)
messagestringyesError or exception message (1 to 5000 characters)
osstringyesOS details (e.g. 'Android 14', 'iOS 17.2')
platformstringyesPlatform (e.g. 'Android', 'iOS', 'Windows')
sdkVersionstringyeshorizOn SDK version, for example '1.8.2' (1 to 50 characters)
sessionIdstringyesThe sessionId registered with horizon_create_crash_session for this app run (1 to 100 characters)
stackTracestring–Full stack trace (max 20000 characters)
typestringyesCrash type: CRASH (fatal), NON_FATAL (exception), or ANR (Application Not Responding)
userIdstring–Player user ID (UUID) from horizon_signup_* or horizon_signin_*, if a player is signed in

No output schema declared.

No examples provided.

horizon_create_crash_session ~296

Registers one app run (session) so horizOn can calculate the crash-free session rate. Use when: once at every app start, with a new unique sessionId; keep that ID for horizon_create_crash_report. Not for: reporting the crash itself (use horizon_create_crash_report) or player sign-in sessions (those come from horizon_signin_*). Side effects: records one session per call; a later crash report with the same sessionId marks it as crashed. Returns: {status: 'ok'}. Errors: 400 when a field is missing or too long: fix it and resend. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
appVersionstringyesApp version (e.g. '1.2.3')
platformstringyesPlatform (e.g. 'Android', 'iOS', 'Windows')
sessionIdstringyesNew unique ID for this app run, for example a UUID generated at start (1 to 100 characters)
userIdstring–Player user ID (UUID) from horizon_signup_* or horizon_signin_*, if a player is signed in

No output schema declared.

No examples provided.

horizon_create_log ~332

Writes one server-side log entry (INFO, WARN or ERROR) for a player, visible in the horizOn dashboard's user logs. Use when: the game records a technical event or a handled error for later debugging, for example a failed purchase or a slow load. Not for: crashes and exceptions with a stack trace (use horizon_create_crash_report) or messages written by players (use horizon_submit_feedback). Requires: a userId from horizon_signup_* or horizon_signin_* and the BASIC tier or higher; no session token. Side effects: creates one log entry per call; a repeat creates a duplicate. Returns: {id, createdAt} of the new entry. Errors: 403 on the FREE tier: logs need BASIC or higher, so upgrade or skip logging. 400 for an invalid userId: pass the ID from the sign-in result. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
errorCodestring–Optional code to group entries in the dashboard, useful with ERROR, for example 'IAP_TIMEOUT' (max 50 characters)
messagestringyesLog message (max 1000 characters)
typestringyesLog level: INFO, WARN or ERROR
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_get_all_localizations ~222

Reads all translated strings of one language as a key to text map (default: the app's default language). Use when: at game start or on a language switch, to load every UI text at once. Not for: a single key (use horizon_get_localization). Side effects: None (read only). Returns: {translations, language, total}; translations is empty (total 0) when the language has no strings. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
langstring–ISO 639-1 language code (2 lowercase letters). One of: en, de, es, fr, it, pt, nl, pl, ru, ja, zh, ar, ko, tr, id. Omit for the app's default language.
NameTypeReqDescription
language–––
total–––
translations–––

No examples provided.

horizon_get_all_remote_configs ~173

Reads every remote config value of the app in one call as a key to string map. Use when: at game start, to cache all flags and balance values at once. Not for: a single value (use horizon_get_remote_config) or checking the API key only (use horizon_test_connection). Side effects: None (read only). Returns: {configs, total}, where configs maps each key to its string value; an app without configs returns an empty map and total 0. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

Input schema present but exposes no named parameters.

NameTypeReqDescription
configs–––
total–––

No examples provided.

horizon_get_email_status ~239

Returns the delivery status (pending, processing, sent or failed) and details of one email queued with horizon_send_email. Use when: checking whether a sent or scheduled email was delivered, or why it failed. Not for: sending again (use horizon_send_email only for a new email) or stopping delivery (use horizon_cancel_email). Requires: the emailId from horizon_send_email. Side effects: None (read only). Returns: {id, status, templateSlug, userId, language, scheduledAt, processedAt, errorReason, createdAt}; errorReason explains a failed email (for example SMTP rejected it). Errors: An unknown emailId or one of another API key is rejected: pass the id returned by horizon_send_email. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
emailIdstringyesEmail ID (UUID) returned by horizon_send_email
NameTypeReqDescription
createdAt–––
errorReason–––
id–––
language–––
processedAt–––
scheduledAt–––
status–––
templateSlug–––
userId–––

No examples provided.

horizon_get_leaderboard_around ~331

Returns the leaderboard entries directly above and below a player's own position, including the player, each with profile. Use when: a game shows 'you and your rivals' instead of the global top. Not for: the global top list (use horizon_get_leaderboard_top) or the position alone (use horizon_get_user_rank). Requires: a userId from horizon_signup_* or horizon_signin_*; no session token. Side effects: None (read only). Returns: {entries: [{position, username, score, profile}]} in board order. profile is {avatarId, frameId, badges} (IDs from the cosmetics catalog; null or [] when not set, set with horizon_set_profile). Errors: 404 for an unknown leaderboardKey: list valid keys with horizon_list_leaderboards. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
leaderboardKeystring–Board key from horizon_list_leaderboards (1 to 64 characters: a-z, 0-9, _ and -). Omit to use the API key's default board.
rangeinteger–How many entries around the player to return, 1 to 50 (default 10)
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
entries–––

No examples provided.

horizon_get_leaderboard_top ~380

Returns the top entries of a leaderboard from position 1, each with the player's profile, plus the requesting player's own entry when they are outside the list. Use when: a game shows a global top list (top 10, top 100). Not for: one player's position alone (use horizon_get_user_rank) or the players around them (use horizon_get_leaderboard_around). Requires: a userId from horizon_signup_* or horizon_signin_* (used to append the player's own entry); no session token. Side effects: None (read only). Returns: {entries: [{position, username, score, profile}]} in board order. When the userId is not among them, the player's own entry with the real position is added at the end. An empty board returns an empty list. profile is {avatarId, frameId, badges} (IDs from the cosmetics catalog; null or [] when not set, set with horizon_set_profile). Errors: 404 for an unknown leaderboardKey: list valid keys with horizon_list_leaderboards. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
leaderboardKeystring–Board key from horizon_list_leaderboards (1 to 64 characters: a-z, 0-9, _ and -). Omit to use the API key's default board.
limitinteger–Number of top entries to return, 1 to 100 (default 10)
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
entries–––

No examples provided.

horizon_get_localization ~271

Reads one translated string by key in one language (default: the app's default language). Use when: the game needs a single text, for example a dynamic message. Not for: loading the UI texts at start (use horizon_get_all_localizations) or finding out which languages exist (use horizon_get_localization_languages). Side effects: None (read only). Returns: {localizationKey, value, language, found}. A missing key or translation is not an error: found is false and value null, so show a fallback text. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
keystringyesLocalization key exactly as defined in the dashboard, for example 'ui.play_button' (max 100 characters)
langstring–ISO 639-1 language code (2 lowercase letters). One of: en, de, es, fr, it, pt, nl, pl, ru, ja, zh, ar, ko, tr, id. Omit for the app's default language.
NameTypeReqDescription
found–––
language–––
localizationKey–––
value–––

No examples provided.

horizon_get_localization_languages ~169

Lists the languages that have at least one translation for the app. Use when: before horizon_get_localization or horizon_get_all_localizations, to pick a valid lang or build a language menu. Not for: the texts themselves (use horizon_get_all_localizations). Side effects: None (read only). Returns: {languages, total}, where languages holds ISO 639-1 codes such as 'en' or 'de'; empty when the app has no translations. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

Input schema present but exposes no named parameters.

NameTypeReqDescription
languages–––
total–––

No examples provided.

horizon_get_news ~264

Lists the published in-game news of the app (announcements, patch notes) created in the horizOn dashboard, newest first. Use when: a game shows a news feed or a message of the day, optionally filtered to the player's language. Not for: configuration values (use horizon_get_remote_config) or UI translations (use horizon_get_localization). Side effects: None (read only). Returns: an array of {id, title, message, releaseDate, languageCode} with only published entries whose release date has passed; an empty array means there is no news. Without languageCode, entries of all languages are returned. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
languageCodestring–ISO 639-1 code (2 lowercase letters, for example 'en' or 'de') to return only entries in that language. Omit for all languages
limitinteger–Maximum number of entries to return, 0 to 100 (default 20)

No output schema declared.

No examples provided.

horizon_get_profile ~345

Returns a player's profile (avatar, frame, badges), the player's unlocks and the cosmetics catalog of the API key with an available flag per entry. Use when: before horizon_set_profile, to show the selection screen and learn which IDs the player may pick. Not for: changing the profile (use horizon_set_profile) or showing other players' profiles (leaderboard entries already carry them). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Side effects: None (read only). Returns: {userId, profile: {avatarId, frameId, badges}, unlocks, cosmetics: [{id, type, locked, available}], limits: {maxBadges, maxUnlocks}}. available true means this player may select the entry. Errors: 401 SESSION_REQUIRED for a missing or expired session: sign in again. 403 SESSION_FORBIDDEN for a session of another user: pass the userId that signed in. 404 PLAYER_NOT_FOUND for an unknown userId. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
cosmetics–––
limits–––
profile–––
unlocks–––
userId–––

No examples provided.

horizon_get_remote_config ~225

Reads one remote config value by key (a feature flag or balance value set in the horizOn dashboard) as a string. Use when: the game needs a single value, for example to check one feature flag. Not for: loading many values at start (use horizon_get_all_remote_configs, one request instead of many). Side effects: None (read only). Returns: {configKey, configValue, found}. Values are always strings: parse numbers and booleans yourself. An unknown key is not an error: found is false and configValue null, so fall back to a default. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
keystringyesConfig key exactly as defined in the dashboard, case-sensitive, for example 'max_lives' (max 256 characters)
NameTypeReqDescription
configKey–––
configValue–––
found–––

No examples provided.

horizon_get_state ~409

Returns the player's server-owned values (Validated Actions): balance, today's earnings and daily cap per value key, such as currency or loot counters. Use when: the game shows the wallet or checks whether a spend is possible before starting a run. Not for: changing balances: nothing writes a balance directly, only accepted runs through horizon_submit_validated with earned. Also not for the save game (use horizon_load_cloud_data). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Side effects: None (read only). Returns: {userId, day, values: [{key, balance, earnedToday, dailyCap}]}: every value key defined in the API key's rules, sorted by key (balance 0 when never earned); values is empty when the rules define no values. day is the current UTC day, earnedToday the positive credit on that day (0 after midnight UTC), dailyCap null without a daily cap; the other value rules stay hidden. Games may mirror the values into cloud save for offline display but must never send them back. Errors: The error result names the stable code: 401 SESSION_REQUIRED (sign in again), 403 SESSION_FORBIDDEN (pass the userId that signed in), 404 PLAYER_NOT_FOUND, 429 without code (account request limit, wait). Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
day–––
userId–––
values–––

No examples provided.

horizon_get_user_rank ~298

Returns one player's own position, score and profile on a leaderboard. Use when: after horizon_submit_score, or to show 'your rank' in the game. Not for: a list of entries (use horizon_get_leaderboard_top or horizon_get_leaderboard_around). Requires: a userId from horizon_signup_* or horizon_signin_*; no session token. Side effects: None (read only). Returns: {position, username, score, profile}. profile is {avatarId, frameId, badges} (IDs from the cosmetics catalog; null or [] when not set, set with horizon_set_profile). Errors: 404 when the player has no score on this board yet (submit one first) or the leaderboardKey is unknown (check horizon_list_leaderboards). Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
leaderboardKeystring–Board key from horizon_list_leaderboards (1 to 64 characters: a-z, 0-9, _ and -). Omit to use the API key's default board.
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
position–––
profile–––
score–––
username–––

No examples provided.

horizon_list_leaderboards ~235

Lists the leaderboard boards of this API key with their keys, sort order and whether they accept only validated runs. Use when: first, when a game has several boards (weekly, per level) or before submitting, to learn the leaderboardKey values and whether a board is validatedOnly. Not for: reading entries (use horizon_get_leaderboard_top, horizon_get_user_rank or horizon_get_leaderboard_around). Side effects: None (read only). Returns: {boards: [{key, name, sortOrder, isActive, scoreCount, validatedOnly}], totalElements}. Pass a key as leaderboardKey to the submit, top, rank and around tools; without it they use the default board. validatedOnly true means scores are accepted only through horizon_start_run plus horizon_submit_validated. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

Input schema present but exposes no named parameters.

NameTypeReqDescription
boards–––
totalElements–––

No examples provided.

horizon_load_cloud_data ~354

Reads a player's save game that was stored with horizon_save_cloud_data. Use when: the game starts or the player switches devices and needs the saved progress. Not for: writing (use horizon_save_cloud_data) or balances of server-owned values (use horizon_get_state). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Side effects: None (read only). Returns: {found, saveData, revision}. found false with saveData null means the player never saved: this is normal on first start, so initialize default data. revision is the save's server revision (0 = empty slot, 1 after the first save): pass it as expectedRevision to horizon_save_cloud_data. revision null means the server sent none (unknown state, for example a self-hosted simpleServer), not an empty slot. Errors: 401 means the session is missing or expired: sign in again and retry with the new accessToken. 403 for a session means the token belongs to another user: pass the userId that signed in. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
found–––
revision–––
saveData–––

No examples provided.

horizon_redeem_gift_code ~452

Redeems a gift code for a player: uses up one redemption and returns the reward data and any cosmetic unlocks. Use when: the player confirms a code in the game. Check it first with horizon_validate_gift_code when unsure. Not for: only checking a code (use horizon_validate_gift_code, which uses nothing up). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Side effects: each successful call uses up one redemption and cannot be undone; cosmetic grants are added to the player's unlocks. A repeat redeems again or fails when the per-player limit is reached. Returns: {success, message, giftData, grantedUnlocks}. giftData is a JSON string with the reward set in the dashboard, which the game must parse and apply. grantedUnlocks lists the cosmetic IDs from giftData.grants the player owns after this redemption ([] without grants); show them with horizon_get_profile. Errors: 404 for an unknown code. 400 for an expired or revoked code or a reached limit: tell the player the code does not work. 409 UNLOCK_LIMIT_REACHED when the player would own more than 25 unlocks; the code is not used up. 401 means the session is missing or expired: sign in again and retry with the new accessToken. 403 for a session means the token belongs to another user: pass the userId that signed in. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
codestringyesGift code as the player typed it (max 50 characters)
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_save_cloud_data ~526

Stores a player's save game as one string (usually JSON) on the server, replacing the player's previous save. Use when: the game saves progress so it survives reinstalls and can be loaded on another device. Pass expectedRevision (the revision from horizon_load_cloud_data or the last save) so a save from another device is never overwritten silently. Not for: reading the save (use horizon_load_cloud_data) or server-owned currency that players must not edit (use Validated Actions: horizon_submit_validated and horizon_get_state). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Side effects: overwrites the player's single save slot; the old save is gone. With expectedRevision it only writes while the save is still at that revision. Saving the same data again changes nothing. Returns: {success: true, dataSizeBytes, revision}. revision is the new revision: pass it as expectedRevision to the next save. Errors: 409 (only with expectedRevision) when another device saved first: nothing was written, load the current save and revision, decide, then save again. 403 when the data exceeds the tier limit (defaults FREE 1 KB, BASIC 5 KB, PRO 20 KB, ENTERPRISE 250 KB): shrink the save or upgrade. 401 means the session is missing or expired: sign in again and retry with the new accessToken. 403 for a session means the token belongs to another user: pass the userId that signed in. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
datastringyesThe complete save as a string, usually serialized JSON (max 300,000 characters; the tier limit is usually lower)
expectedRevisioninteger–Optional. Revision the save must still have, sent as If-Match: 0 for an empty slot, else the revision from horizon_load_cloud_data or the last save. Omit for an unconditional overwrite.
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_send_email ~422

Queues a transactional email from a dashboard template to one registered player, sent now or at a scheduled time through the developer's own SMTP server. Use when: the game triggers a message to a player, such as a welcome mail or a reminder. Not for: checking delivery (use horizon_get_email_status) or stopping a queued email (use horizon_cancel_email). Requires: an SMTP configuration and the template in the horizOn dashboard, and a recipient with a verified email address. Side effects: queues one new email per call; repeating the call sends the email again, so never repeat it to retry a status check. Returns: {id, status: 'pending', scheduledAt}; pass id to horizon_get_email_status to follow delivery or to horizon_cancel_email to stop it. Errors: 400 for an unknown templateSlug or a recipient without a verified email address: check the slug in the dashboard or pick another user. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
languagestringyesISO 639-1 code (2 lowercase letters) of the template translation to render, for example 'en' or 'de'
scheduledAtstring–ISO 8601 timestamp for delayed delivery, at most 30 days ahead, for example 2026-10-01T09:00:00Z. Omit to send immediately
templateSlugstringyesSlug of an email template defined in the dashboard, for example 'welcome' or 'reminder'
userIdstringyesPlayer user ID (UUID) of the recipient; the player needs a verified email address
variablesobjectyesValues for the template's variables as a string map, for example {"username": "John"}; pass {} when the template has none

No output schema declared.

No examples provided.

horizon_set_profile ~536

Sets a player's avatar, frame and displayed badges. This REPLACES THE WHOLE PROFILE: an omitted, null or empty slot is cleared. Use when: the player confirms a new look. Pass the current values from horizon_get_profile for the slots that should stay. Not for: reading the profile or the catalog (use horizon_get_profile). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Every ID must be in the API key's catalog, have the type of its slot and be free or unlocked for the player; at most 3 distinct badges. Side effects: overwrites all three slots; the same call again changes nothing more. The profile shows up in leaderboard entries. Returns: the same body as horizon_get_profile after the change. Errors: The error result names the stable code. 400 INVALID_BADGES, INVALID_COSMETIC_ID, COSMETIC_NOT_FOUND or COSMETIC_TYPE_MISMATCH: pick IDs of the right type from horizon_get_profile. 403 COSMETIC_LOCKED: the cosmetic is locked and the player has not unlocked it (unlocks come from gift code grants). 401 SESSION_REQUIRED for a missing or expired session: sign in again. 403 SESSION_FORBIDDEN for a session of another user: pass the userId that signed in. 404 PLAYER_NOT_FOUND for an unknown userId. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
avatarId––Cosmetic ID of type avatar from the catalog (for example "avatar.gold"). Omit, null or "" clears the avatar slot.
badgesarray–Badge cosmetic IDs of type badge to display (0 to 3, distinct, order kept, each 1 to 32 characters: a-z, 0-9, ., _ and -). Omit or [] clears all badges.
frameId––Cosmetic ID of type frame from the catalog (for example "frame.gold"). Omit, null or "" clears the frame slot.
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_signin_anonymous ~303

Signs in an anonymous (guest) account with its stored anonymousToken and starts a player session (accessToken). Use when: a returning guest opens the game, or right after horizon_signup_anonymous, before any tool that needs sessionToken. Not for: email accounts (use horizon_signin_email) or creating a guest (use horizon_signup_anonymous). Requires: the anonymousToken returned by horizon_signup_anonymous for this account. Side effects: creates a new session on every call; older sessions stay valid. Returns: {userId, username, accessToken, authStatus, message}. Pass userId and accessToken (as sessionToken) to the session tools; the token stays valid for one hour after its last use. Errors: 404 for an unknown anonymousToken: the account is lost, create a new one with horizon_signup_anonymous. 429 also comes from the per-token limit (5 attempts per minute) and the per-IP limit (30 per minute): wait a minute. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
anonymousTokenstringyesanonymousToken returned by horizon_signup_anonymous (exactly 32 characters: letters, digits, _ and -)

No output schema declared.

No examples provided.

horizon_signin_email ~303

Signs in an existing email account with email and password and starts a player session (accessToken). Use when: a registered, verified player logs in, before any tool that needs sessionToken (submit score, cloud save, gift code redeem, profile, validated runs). Not for: guest accounts (use horizon_signin_anonymous), new players (use horizon_signup_email) or checking an existing token (use horizon_check_auth). Side effects: creates a new session on every call; older sessions stay valid. Returns: {userId, username, email, accessToken, authStatus, message}. Pass userId and accessToken (as sessionToken) to the session tools; the token stays valid for one hour after its last use. Errors: 401 for a wrong password or unknown email: ask the player to check the credentials. 403 (NOT_VERIFIED) for an unverified email: the player must confirm the verification email first. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
emailstringyesEmail address the account was registered with (max 40 characters)
passwordstringyesAccount password (max 128 characters); sent to the API only, never returned

No output schema declared.

No examples provided.

horizon_signup_anonymous ~266

Creates a new anonymous (guest) player account that needs only a display name and returns its anonymousToken. Use when: a player starts without email, for example on first launch of a game with guest play. Not for: players who sign up with email and password (use horizon_signup_email) or returning guests (use horizon_signin_anonymous with the stored anonymousToken). Side effects: creates one new player account per call; calling it again creates another account, it never finds an existing one. Sign-up starts no session. Returns: {userId, username, isAnonymous: true, anonymousToken, createdAt}. Store anonymousToken on the device: it is the only way back into this account. Then call horizon_signin_anonymous to get the accessToken that session tools need. Errors: 400 for an invalid displayName: fix the name and retry. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
displayNamestringyesDisplay name shown on leaderboards (max 30 characters)

No output schema declared.

No examples provided.

horizon_signup_email ~321

Creates a new player account with email, password and display name; the account starts unverified and without a session. Use when: a new player registers with email so the account can be recovered on other devices. Not for: existing accounts (use horizon_signin_email) and guest play without email (use horizon_signup_anonymous). Side effects: creates one player account and sends the verification email. It does not sign in. Returns: the new user with userId and isVerified: false. After the player verified the email address, call horizon_signin_email to get the accessToken. Errors: 409 when the email already belongs to an account: sign in with horizon_signin_email instead. 400 for an invalid email, a password outside 8 to 128 characters or a common password, or a bad name: fix the value and retry. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
displayNamestringyesDisplay name shown on leaderboards (max 30 characters)
emailstringyesPlayer email address (valid format, max 40 characters); must not be registered yet
passwordstringyesPassword, 8 to 128 characters; common passwords such as "password1" are rejected. Sent to the API only, never returned

No output schema declared.

No examples provided.

horizon_start_run ~848

Starts a server-checked run (Validated Actions): issues a single-use ticket with a server seed and starts the server's run timer. Use when: before a round whose score or earned currency must be checked by the server, and always for boards marked validatedOnly in horizon_list_leaderboards. Not for: normal boards where the client may write its own score (use horizon_submit_score) and reading balances (use horizon_get_state). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Pass leaderboardKey to bind the ticket to a board (it must exist; 'default' is created on first use). Optionally declare the start context (gameVersion, contentVersion, simulationVersion, replayFormatVersion, contentDigest, initialStateBase64); the server binds it with its own start values (rule version, cloud save, balances, seed) into the run, and a sus run keeps it in its review package. Side effects: issues a new ticket per call and counts against the run limits per player and per account; unused tickets simply expire. Returns: {runId, ticket, seed, leaderboardKey, issuedAt, expiresAt, expiresInSeconds}. Seed the game's deterministic randomness with seed, record the input log, then send the result with horizon_submit_validated and the unchanged ticket before expiresAt. Errors: Errors carry a stable code in the body and the error result names it: 400 for a bad context field, 400 INITIAL_STATE_INVALID_ENCODING (initialState not standard base64), 413 INITIAL_STATE_TOO_LARGE (decoded initialState above the plan's evidenceMaxBytes), 401 SESSION_REQUIRED, 403 SESSION_FORBIDDEN, 404 PLAYER_NOT_FOUND or LEADERBOARD_NOT_FOUND, 429 RUN_RATE_LIMITED (per player and hour) or RUN_CAPACITY_REACHED (account per UTC hour; do not retry automatically, the wait can be an hour), 503 VALIDATED_ACTIONS_UNAVAILABLE (server has no ticket key). After 401 sign in again; after 403 SESSION_FORBIDDEN pass the userId that signe…

NameTypeReqDescription
contentDigeststring–SHA-256 of the game content the run uses, 64 hex characters (sent in lower case). Optional
contentVersionstring–Version of the game content (levels, balancing data) the run uses (1 to 64 printable ASCII characters), stored in the run's start context. Optional
gameVersionstring–Game build version, for example "1.4.2" (1 to 64 printable ASCII characters), stored in the run's start context. Optional
initialStateBase64string–Bytes the simulation starts from as standard base64 (sent as context.initialState); decoded at most the plan's evidenceMaxBytes (32,768 bytes by default). Optional
leaderboardKeystring–Board key to bind the ticket to, from horizon_list_leaderboards (1 to 64 characters: a-z, 0-9, _ and -). Omit for an unbound ticket (currency only runs, or choose the board at submit).
replayFormatVersionstring–Format version of the recorded input log (1 to 64 printable ASCII characters), stored in the run's start context. Optional
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
simulationVersionstring–Version of the deterministic simulation that replays the input log (1 to 64 printable ASCII characters), stored in the run's start context. Optional
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_submit_feedback ~380

Sends a message written by a player (bug report, feature request or comment) to the developer's feedback inbox in the horizOn dashboard. Use when: the player fills in a feedback or bug report form in the game. Not for: crashes and caught exceptions (use horizon_create_crash_report) or technical events the game logs by itself (use horizon_create_log). Requires: a userId from horizon_signup_* or horizon_signin_*; no session token. Side effects: creates one feedback entry per call; a repeat creates a duplicate. Returns: the text "ok" when the entry was stored. Errors: An unknown userId, or one of another API key, is rejected: pass the ID from the sign-in result. 403 when the account's feedback limit is reached: delete old feedback in the dashboard. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
categorystring–Free text category (max 50 characters); the horizOn SDKs use BUG, FEATURE and GENERAL. Omit for none
deviceInfostring–Device and OS details for bug reports, for example 'Pixel 8, Android 14' (max 500 characters)
emailstring–Contact email address for a reply, if the player wants one
messagestringyesFull feedback text (1 to 2048 characters)
titlestringyesShort summary shown in the dashboard list (1 to 100 characters)
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_submit_score ~468

Submits a player's score to a leaderboard; the board keeps only each player's best score (higher on DESC boards, lower on ASC boards). Use when: a round ends on a normal board. Then call horizon_get_user_rank to show the new position. Not for: boards marked validatedOnly in horizon_list_leaderboards (use horizon_start_run and horizon_submit_validated) and reading scores (use horizon_get_leaderboard_top). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. Side effects: replaces the player's entry only when the new score beats it; a worse or equal score, or the same call again, changes nothing. Returns: {success: true} (also when the score did not beat the best one). Errors: 403 VALIDATED_SUBMIT_REQUIRED on a validatedOnly board, nothing written: switch to horizon_start_run and horizon_submit_validated, do not retry. 403 PLAYER_BANNED when a moderator banned the player from the board: final, do not retry. 404 for an unknown leaderboardKey: list valid keys with horizon_list_leaderboards. 401 means the session is missing or expired: sign in again and retry with the new accessToken. 403 for a session means the token belongs to another user: pass the userId that signed in. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
leaderboardKeystring–Board key from horizon_list_leaderboards (1 to 64 characters: a-z, 0-9, _ and -). Omit to use the API key's default board.
scoreintegeryesScore to submit, a non-negative integer
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_submit_validated ~1,302

Submits the result of a run started with horizon_start_run; the server checks ticket and server-only rules before it writes the score and earned values. Use when: a validated run ends: send the ticket, the claimed score (and stage) and the SHA-256 commitment of the input log. Not for: runs without a ticket (use horizon_submit_score on normal boards) and reading balances (use horizon_get_state). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. The ticket from horizon_start_run and exactly one of inputLogHash (a ready SHA-256), inputLogBase64 (raw log bytes, hashed locally) or inputLog (UTF-8 text, hashed locally); the log itself is never sent. score is required when a board is targeted (the ticket's board, or leaderboardKey) and ignored otherwise. Send earned ({key, amount}[]) only when the API key's rules define values: positive amounts earn, negative amounts spend. Side effects: consumes the ticket (single use, also when rejected); an accepted run writes the score (best score kept) and changes the balances of earned values. The same ticket cannot be submitted twice. Returns: {accepted, runId, leaderboardKey, score, bestScore, isNewHighScore, rank, durationSeconds, state, evidence, sus} plus inputLogHash (the hash that was sent). sus is true when the run was accepted but crossed a soft threshold of the rules (the flag names stay on the server); the run counts normally and the server keeps a review package. Rejected runs are never sus; an older server without the field reads as false. state is the player's server-owned values after the run: {day, values: [{key, balance, earnedToday, dailyCap, requested, credited}]}, every value key of the rules sorted by key; requested (amount sent) and credited (amount applied) appear only for the keys in earned. credited < requested on a positive amount means dailyCap or maxBalance clamped it (not an error). For a spend, credited is e…

NameTypeReqDescription
earnedarray–Earned or spent server-owned values ({key, amount}, at most 64, each key once). Every key must be defined in the values of the API key's rules (else UNKNOWN_VALUE_KEY). Omit when the game uses no ser…
inputLogstring–Input log as UTF-8 text; the tool hashes its UTF-8 bytes locally. Use this or inputLogHash or inputLogBase64.
inputLogBase64string–Raw input log bytes as base64; the tool computes the SHA-256 locally. Use this or inputLogHash or inputLog.
inputLogHashstring–SHA-256 of the raw input log bytes, 64 hex characters. Use this or inputLogBase64 or inputLog.
leaderboardKeystring–Target board. Omit to use the board the ticket is bound to; required for a leaderboard write with an unbound ticket.
scoreinteger–Claimed score (0 to 9007199254740991). Required when a board is targeted, ignored for a run without board.
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
stagestring–Stage key for stage rules (for example "level-3")
ticketstringyesticket from horizon_start_run, sent unchanged (opaque, about 190 characters, max 512)
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_test_connection ~215

Checks that HORIZON_API_KEY and HORIZON_BASE_URL reach the horizOn API by fetching the app's remote configs; takes no input. Use when: first during setup, and whenever other tools fail, to tell a configuration problem from a problem of the call itself. Not for: checking a player's session (use horizon_check_auth) or reading config values for the game (use horizon_get_all_remote_configs). Side effects: None (read only). Returns: {success: true, data} where data holds the app's remote configs; success means key and URL work. Errors: A network error means HORIZON_BASE_URL is wrong or unreachable: check the URL (default https://horizon.pm). Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

Input schema present but exposes no named parameters.

NameTypeReqDescription
data–––
success–––

No examples provided.

horizon_upload_evidence ~652

Uploads the input log of a validated run after horizon_submit_validated answered with evidence.required = true (evidence review). Use when: only when a submit result carries evidence.required: true, before evidence.uploadBefore (24 hours). Not for: runs without an evidence request (the server answers 404 EVIDENCE_NOT_REQUESTED) and sending the log with the submit (the submit takes only its hash). Requires: userId and the player's session: sign in with horizon_signin_email or horizon_signin_anonymous and pass its accessToken as sessionToken. The runId from the submit result and the raw input log as exactly one of inputLogBase64 (raw bytes as base64) or inputLog (UTF-8 text). Its SHA-256 must equal the inputLogHash sent with the run, so pass the same bytes or text that were hashed at submit; at most evidence.maxBytes (32,768 bytes by default). Side effects: stores the log for the developer's review; one upload per run (a second one gets 409). Returns: {runId, status: "UPLOADED", bytes} plus inputLogHash (the SHA-256 of the uploaded bytes, for comparison with the submit). Errors: Errors carry a stable code in the body and the error result names it: 400 EVIDENCE_INVALID_ENCODING (not standard base64), 401 SESSION_REQUIRED, 403 SESSION_FORBIDDEN, 404 EVIDENCE_NOT_REQUESTED (no request for this run and player; also for a run of another player), 409 EVIDENCE_ALREADY_UPLOADED, 410 EVIDENCE_EXPIRED (window passed, slot freed), 413 EVIDENCE_TOO_LARGE (decoded log above maxBytes), 422 EVIDENCE_HASH_MISMATCH (SHA-256 of the log differs from the run's inputLogHash; the request stays open, retry with the correct bytes until uploadBefore), 429 without code (account request limit). Only 422 and network errors are worth a retry; the others are final. Local error (no request sent): INVALID_INPUT_LOG when the log input was missing, doubled, empty or not base64. Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_tes…

NameTypeReqDescription
inputLogstring–Input log as UTF-8 text; its UTF-8 bytes are uploaded. Use this or inputLogBase64.
inputLogBase64string–Raw input log bytes as standard base64. Use this or inputLog.
runIdstringyesrunId (UUID) from the horizon_submit_validated result (evidence.runId)
sessionTokenstringyesaccessToken from horizon_signin_email or horizon_signin_anonymous for this userId; sent as a Bearer session
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*

No output schema declared.

No examples provided.

horizon_validate_gift_code ~255

Checks whether a gift code can be redeemed by this player right now, without using it up. Use when: before horizon_redeem_gift_code, for example to confirm a code the player typed and show an error early. Not for: claiming the reward (use horizon_redeem_gift_code; validating alone grants nothing). Requires: a userId from horizon_signup_* or horizon_signin_*; no session token. Side effects: None (read only); no redemption is used. Returns: {valid}. valid false means the code does not exist, has expired, was revoked or reached its limit for this player or in total; tell the player the code does not work. Errors: Needs HORIZON_API_KEY. Every failure returns an error result (isError) with the HTTP status and body: a 401 that horizon_test_connection also returns means HORIZON_API_KEY is wrong, 429 means the account's per-minute rate limit was reached (wait a minute, then retry).

NameTypeReqDescription
codestringyesGift code as the player typed it (max 50 characters)
userIdstringyesPlayer user ID (UUID) from horizon_signup_* or horizon_signin_*
NameTypeReqDescription
valid–––

No examples provided.

Install

How do I install the horizOn MCP server?

horizOn runs locally as an npm package, launched with npx -y horizon-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

npm · horizon-mcp

# add to Claude Code
claude mcp add projectmakersde-horizon-mcp -- npx -y horizon-mcp
Common questions

What is the horizOn MCP server?

horizOn is an MCP server listed in the public MCP registry as io.github.ProjectMakersDE/horizon-mcp. Docs, live API tools and prompts for the horizOn game backend in Godot, Unity and Unreal. This page covers its npm package (horizon-mcp).

Is the horizOn MCP server safe to use?

horizOn scores 89 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 16 October 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

What tools does the horizOn MCP server expose?

horizOn exposes 34 tools: horizon_test_connection, horizon_signup_anonymous, horizon_signup_email, horizon_signin_email, horizon_signin_anonymous, and 29 more. Their descriptions and schemas cost roughly 12,938 tokens of context every time the server is loaded.

Is the horizOn MCP server still maintained?

horizOn is still listed as active in the MCP registry. We last reached this channel on 16 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

What licence is the horizOn MCP server under?

horizOn declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.