Integrating the Gamescript feed
Everything an operator's install needs to take titles from the feed. Your game server keeps the random numbers, the outcomes and the wallet. Gamescript supplies the content.
The model
A Gamescript title is a pack: the maths config, a maths document, the art set and the sound. You import the pack into your own game catalogue and your own game server plays it. Gamescript is not called during a round.
There are two hosts.
| Host | What it serves | Auth |
|---|---|---|
cdn.gamescript.net | The catalogue, a public manifest per title, all art and sound | None |
api.gamescript.net | Title packs with reel strips, maths documents, usage reports | Operator key |
1. Read the catalogue
GET https://cdn.gamescript.net/feed/v1/catalogue.json
GET https://cdn.gamescript.net/feed/v1/catalogue.sig
The catalogue lists every title with its current version, headline facts, the URL of its public manifest and the SHA-256 of its pack. catalogue.sig is a base64 Ed25519 signature over the exact bytes of catalogue.json.
Poll it on a schedule. It is cached for five minutes, so polling more often gains nothing. A title is new or changed when its version differs from the one you hold.
2. Pin the studio key
GET https://cdn.gamescript.net/feed/v1/studio-key.json
Store the public key in your own configuration once, at setup. Do not re-read it from the feed each time you verify: a key fetched from the same place as the data proves nothing. The signing key is held offline at the studio and is never present on the delivery network.
3. Fetch the pack
GET https://api.gamescript.net/v1/titles/{slug}/pack
GET https://api.gamescript.net/v1/titles/{slug}/pack?version={version}
Authorization: Bearer gs_...
The body is the pack as JSON. Three response headers carry the proof:
| Header | Meaning |
|---|---|
x-gamescript-version | The pack version, a 12 character content hash |
x-gamescript-sha256 | SHA-256 of the response body |
x-gamescript-signature | Ed25519 signature over the response body, base64 |
Verify the signature over the raw body bytes with your pinned key before you parse anything. Reject the pack if it fails.
import base64, urllib.request
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
PINNED = "base64 public key from your own config"
req = urllib.request.Request(
"https://api.gamescript.net/v1/titles/safari-reels/pack",
headers={"Authorization": "Bearer gs_..."})
with urllib.request.urlopen(req) as r:
body = r.read()
signature = base64.b64decode(r.headers["x-gamescript-signature"])
key = Ed25519PublicKey.from_public_bytes(base64.b64decode(PINNED))
key.verify(signature, body) # raises if the pack was altered
The maths document is at /v1/titles/{slug}/math with the same key.
4. What is in a pack
| Field | Content |
|---|---|
schema | gamescript.pack/1 |
kind | slot |
slug, version, publishedAt | Identity. A version never changes meaning |
config | Grid, symbols with pays, paylines, features, RTP in basis points, volatility, bet limits, maximum win, and the reel strips |
world | Presentation hints: ambience and accent colour |
art | Every art and sound file with its SHA-256 and size |
audio | Which of those files are the sound: music ({"file": "music.m4a"} or null), kit (cue name to file for spin, reel, tick, win, bigwin; only the cues the pack has) and voice (line name to file, {} when there are none). null when a title has no sound |
mathSha256 | Hash of the maths document |
Version 1 packs are slots on a 5 by 3 grid with 20 paylines, ten symbols (four low, four high, wild, scatter) and reel strips of 40 to 64 positions. The config object is the Spin script’s SlotTitleConfig without assetBase, which your importer sets to wherever it stores the art.
Download each art file from the artBase in the public manifest and check it against the hash in the pack. Sound files (.m4a) live beside the art under the same artBase and are in the same art list, so one loop fetches and verifies both. The public manifest carries the same audio object, and each catalogue entry has hasAudio. Versioned URLs are immutable and can be cached for good.
5. Go live your way
What happens after import is your decision and your regulator’s. A social or free play operator can activate a verified title at once. A licensed operator will usually stage it as a draft and release it with the next signed build, because outcome-affecting files belong in the release manifest.
6. Report usage
POST https://api.gamescript.net/v1/usage
Authorization: Bearer gs_...
Content-Type: application/json
{
"day": "2026-10-01",
"rows": [
{ "slug": "safari-reels", "rounds": 18230, "stake": "9115000", "win": "8742100", "currency": "GC" }
]
}
One request per day. stake and win are whole numbers sent as strings, in your own coin or currency unit. Sending a day again replaces the earlier report. No round data and no player data is accepted.
Other endpoints
| Endpoint | Returns |
|---|---|
GET /v1/health | Service status and the number of titles |
GET /v1/me | Your operator record and entitlements |
GET /v1/titles | The titles you are entitled to, with version, hash and signature |
Errors
Errors are JSON: { "ok": false, "error": { "code": "...", "message": "..." } }.
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, malformed or suspended key |
| 403 | not_entitled | Your key does not cover that title |
| 404 | no_such_title | Unknown slug or version |
| 400 | bad_report, bad_row | A usage report did not match the shape above |