HTTP & external backends¶
HTTP.* lets your world call an approved backend provider from server scripts — most usefully your own
database for things that must survive a restart: player profiles, progression, leaderboards, rewards. (For
"this session" state, use Storage — it's faster and needs no backend.)
HTTP runs server‑side only, so endpoints and keys are used on the server, never shipped to players.
Only approved providers can be reached
Outbound HTTP is allow‑listed. Your scripts can reach only the providers below — not an arbitrary
third‑party API, not a custom domain or IP you type in, and not the local machine or your own server.
Any other destination is refused with a 403 before the request is sent. This is enforced on the server and
cannot be turned off by a world.
Approved providers¶
| Provider | Main use cases | Auth type | Base URL example | Notes / best practices |
|---|---|---|---|---|
| Supabase | Postgres DB, Auth, Storage, Realtime | supabase (preferred) |
https://your-project.supabase.co |
Strong Row Level Security (RLS). Top recommendation. |
| Firebase (Google) | Realtime Database, Auth, Storage, Functions | bearer |
https://your-project.firebaseio.com (or project‑specific) |
Generous free tier; excellent for realtime features. |
| PlayFab (Microsoft) | Game backend (leaderboards, economy, player data) | bearer or apikey |
https://titleId.playfabapi.com |
Built specifically for games. Key player data by their stable account id — Player.GetUserId(id). |
| Nhost | Postgres + GraphQL | bearer |
https://your-subdomain.nhost.run |
Good Supabase alternative with GraphQL support. |
| Neon | Serverless Postgres | bearer (or key via HTTP.Secret) |
https://your-project.neon.tech |
Modern Postgres with branching. Database‑focused. |
Reachable hosts are the providers' own domains — *.supabase.co, *.firebaseio.com / *.firebasedatabase.app /
*.cloudfunctions.net, *.playfabapi.com, *.nhost.run, *.neon.tech. The Auth type column maps to a scheme in
the Provider Manager (below).
1. Store a credential (Provider Manager)¶
Open Window ▸ Social Scape ▸ Provider Manager, Add a provider with Type = HTTP (credential):
| Field | Meaning |
|---|---|
| Handle | A name your scripts reference, e.g. my-db. |
| Base URL | The API root — must be one of the approved providers above, e.g. https://YOUR-PROJECT.supabase.co. |
| Auth | How the key is attached (see the table below). |
| API Key | Stored encrypted on your account; never displayed again. |
Never put an API key in a script
World bundles aren't encrypted — anything in a script is readable by others. So you don't paste keys into headers. Instead you store the key in the Provider Manager (encrypted on your account) and reference it by handle; the server injects the key at request time. Keys never travel in your world.
Auth schemes¶
| Scheme | What it sends | Use for |
|---|---|---|
bearer |
Authorization: Bearer <key> |
most REST APIs — Firebase, PlayFab, Nhost, Neon |
apikey |
apikey: <key> |
services using an apikey header |
supabase |
both apikey: <key> and Authorization: Bearer <key> — the same key |
Supabase (it wants both; one key is correct) |
header |
<YourHeaderName>: <key> |
a single custom header, e.g. X-Api-Key (PlayFab title key) |
none |
nothing — you place the key yourself | anything unusual (below) |
Supabase uses one key in two headers
Supabase's server pattern puts the same anon/service key in both apikey and Authorization: Bearer. So you
only enter one key — supabase auth sends it to both. (Want a different Authorization value, like a user
JWT? Use none + HTTP.Secret, below.)
2a. Call it by handle — HTTP.*Via¶
The server resolves the handle → base URL + auth, prepends the base URL to your path, and sends. Your script never sees the key or the URL:
using System.Collections;
using UnityEngine;
using NexusVM.Unity;
class Leaderboard : NexusBehaviour
{
IEnumerator SaveScore(string accountId, int score)
{
string body = "{\"user_id\": \"" + accountId + "\", \"score\": " + score + "}";
var result = HTTP.PostVia("my-db", "/rest/v1/scores", body);
yield return result; // wait for the request
if (result.Success) Debug.Log("Saved: " + result.Body);
else Debug.LogError("Failed (" + result.StatusCode + "): " + result.Error);
}
}
| Method | Signature |
|---|---|
| GET | HTTP.GetVia(handle, path[, headers]) |
| POST/PUT/PATCH | HTTP.PostVia/PutVia/PatchVia(handle, path, body[, headers]) |
| DELETE | HTTP.DeleteVia(handle, path[, headers]) |
2b. Reference just the secret — HTTP.Secret¶
For a provider whose auth the schemes don't cover (a key in the query string, a user JWT, two separate keys), keep the
full‑URL call and drop the key into a header with HTTP.Secret(handle). It returns an opaque reference; the real key
is substituted server‑side at send‑time and is never readable in your script:
var headers = new Dictionary<string, string>();
headers["Authorization"] = "Bearer " + HTTP.Secret("my-neon-key");
// The URL must still be an approved provider host (here, Neon):
var result = HTTP.Get("https://your-project.neon.tech/sql", headers);
yield return result;
Raw calls (HTTP.Get(url) …)¶
HTTP.Get/Post/Put/Patch/Delete(url[, body][, headers]) take a full URL — but that URL must still be an approved
provider (the same allow‑list applies). There is no way to reach an arbitrary or public URL; use *Via for anything
that needs a stored key.
Reading the result¶
Every call returns an HTTPAsyncResult you yield return, then read:
Success (bool) · StatusCode (int) · Body (response string) · Error (string).
Letting an NPC answer from a request
Calling HTTP from inside an NPC action? Mark the action Async so the NPC waits for the result and speaks it in one turn (e.g. "what's my score?"). See Custom actions ▸ Async actions.
Examples by provider¶
Store an HTTP credential (handle my-db) with the provider's base URL + auth, then:
// Supabase (auth: supabase) — PostgREST
HTTP.GetVia ("my-db", "/rest/v1/profiles?user_id=eq." + accountId);
HTTP.PostVia("my-db", "/rest/v1/profiles", json);
// Firebase Realtime DB (auth: bearer) — REST is path + ".json"
HTTP.GetVia ("my-db", "/scores/" + accountId + ".json");
HTTP.PutVia ("my-db", "/scores/" + accountId + ".json", json);
// PlayFab (auth: header, X-SecretKey) — server API
HTTP.PostVia("my-db", "/Server/UpdatePlayerStatistics", json);
// Nhost (auth: bearer) — GraphQL
HTTP.PostVia("my-db", "/v1/graphql", "{\"query\":\"{ scores { user_id score } }\"}");
// Neon (auth: bearer) — serverless SQL over HTTP
HTTP.PostVia("my-db", "/sql", "{\"query\":\"select score from scores where user_id = $1\",\"params\":[\"" + accountId + "\"]}");
Always key durable records by the player's stable account id (Player.GetUserId(id)) —
not by Player.IdOf / Networking.GetPlayers, which return a per‑session id that changes on reconnect. (Tip:
scope your database with Row Level Security / permissions — defence in depth.)
Usage guidelines¶
- Only the approved providers above can be reached, via the Provider Manager. Arbitrary URLs, custom hosts/IPs, and the local machine are blocked.
- Always use credential handles (
*Via/HTTP.Secret) — never paste a raw API key into a script. - Enable Row Level Security / permissions on your backend where possible.
- All calls are server‑side only — they don't run in the offline tester, and clients never see your endpoints or keys.
When to use what¶
| Need | Use |
|---|---|
| Counter/flag for the current session | Storage |
| Data that survives restarts (profiles, leaderboards) | HTTP → an approved provider (via a credential) |
| A provider needing a key | a Provider Manager HTTP credential + *Via / HTTP.Secret |