Build slot art at production scale

One API for generating, editing, searching, approving and shipping slot-symbol art — with GLI-ready audit, multi-tenant billing and webhook integrations to your CI.

Introduction

All paths are prefixed with https://kobowlotto.com. JSON in, JSON out (PDFs and ZIPs return binary).

Production base URL

https://kobowlotto.com

Auth

X-API-Key header or Google SSO session cookie.

Rate limit

Generations consume your monthly quota; 5xx + 429 trigger SDK auto-retry.

Versioning

Endpoints are stable. Breaking changes ship behind /api/studio/v2/.

Authentication

Two parallel auth paths — both pass through the same RBAC layer.

api key X-API-Key header

Mint a key in /products/slotforge/api-keys.html. Keys are workspace-scoped and revocable. Use this from servers, CI, scripts.

curl -H "X-API-Key: sf_xxxxxxxxxxxxxxxxx" https://kobowlotto.com/api/studio/health

SSO Google OAuth

For the dashboard. Visit /products/slotforge/login.html, click Sign in with Google; you'll get an sf_session HttpOnly cookie. The SDK uses it automatically in the browser.

Roles: viewer (read), designer (create assets), approver (sign off assets), admin (everything). Endpoints that mutate state declare their minimum role inline below.

Generate

Text-to-image, edit-image and upscale. Every generation is logged to cost_log, increments your monthly quota, and produces a hashed audit row.

Generate from prompt

POST/api/studio/generate_raw

Single-image generation from a text prompt. Counts against monthly quota.

designer · approver · admin
ParamTypeRequired
promptstringyes
sizestring (e.g. 1024x1024)no
brand_slugstringno
providerstringno
formatstring (canonical key)no
curl -X POST https://kobowlotto.com/api/studio/generate_raw \
  -H "X-API-Key: $SF_KEY" \
  -H "content-type: application/json" \
  -d '{"prompt":"a glowing mermaid coin, polished gold","size":"1024x1024"}'
const sf = new SlotForge({ apiKey: process.env.SLOTFORGE_API_KEY });
const r = await sf.gen({
  prompt: 'a glowing mermaid coin, polished gold',
  size: '1024x1024',
});
console.log(r.file, r.cost_cents);
{
  "ok": true,
  "asset_id": 4978,
  "file": "atlantis_mermaid_v1_4978.png",
  "url": "/twingaming_library/atlantis_mermaid_v1_4978.png",
  "size": "1024x1024",
  "sha": "a1b2c3d4",
  "provider": "openai_gpt_image_1",
  "cost_cents": 4,
  "duration_ms": 8200
}

Generate with references

POST/api/studio/generate_with_refs

Condition on existing asset(s) (style transfer, brand match).

designer · approver · admin
curl -X POST https://kobowlotto.com/api/studio/generate_with_refs \
  -H "X-API-Key: $SF_KEY" -H "content-type: application/json" \
  -d '{"prompt":"same character, holding crown","reference_asset_ids":[4865,4866]}'

Edit an asset

POST/api/studio/edit_image

Apply prompt-driven edit to an existing asset. Creates a new kind=edit version in the version timeline.

sf.edit({ asset_id: 4865, prompt: 'add bubbles and emerald tint' })

Upscale

POST/api/studio/upscale

2x / 4x upscale via Real-ESRGAN.

sf.upscale({ asset_id: 4865, scale: 2 })

Search

Semantic library search

POST/api/studio/search_semantic

Vector similarity over CLIP embeddings of every asset. Great for "find every mermaid I've ever generated".

const hits = await sf.search('mermaid gold coin', { limit: 20 });
hits.results.forEach(r => console.log(r.file, r.score));

Projects

List projects

GET/api/studio/projects
await sf.projects.list();

Create project

POST/api/studio/projects
designer · approver · admin
await sf.projects.create({
  slug: 'atlantis_mermaid_v2',
  name: 'Atlantis Mermaid v2',
  theme: 'mermaid',
});

Run math simulation

POST/api/studio/projects/:id/simulate

Monte Carlo simulation: returns RTP, hit frequency, volatility class, biggest win.

await sf.projects.simulate(1, { spins: 10000 });

Brands

List brands

GET/api/studio/brands
await sf.brands.list();

Create brand

POST/api/studio/brands
designer · approver · admin
await sf.brands.create({
  slug: 'pollard',
  name: 'Pollard Banknote',
  palette: { primary: '#1e3a8a', accent: '#fbbf24' },
});

Approval & versions

Change asset status

POST/api/studio/assets/:id/status

draft → review → approved → cert_submitted → production. Each transition is Ed25519-signed.

approver · admin
await sf.assets.approve(4865, 'ready for cert');

Version timeline

GET/api/studio/assets/:id/versions
GET/api/studio/assets/:id/versions/:n/diff?against=:m
POST/api/studio/assets/:id/revert/:n

Git-like history per asset: every edit/regenerate becomes a new version. Diff returns a pixel-difference PNG. Revert creates a new HEAD version cloned from version N.

const versions = await sf.assets.versions(4865);
const diff = await sf.assets.versionDiff(4865, 3, 1);
await sf.assets.revert(4865, 2);

Compliance

Generate GLI report PDF

POST/api/studio/projects/:id/compliance_report

Returns a 30+ page signed PDF: paytable, RTP, asset manifest, audit trail, jurisdiction-specific notes. Supports MN / ON / UK / MX / BR.

designer · approver · admin
const pdf = await sf.projects.complianceReport(1, { jurisdiction: 'MN' });
fs.writeFileSync('atlantis_MN.pdf', pdf);

Export

Sprite atlas

POST/api/studio/export_sprite_atlas

Packs every approved asset of a project into one PNG + JSON manifest.

await sf.exports.spriteAtlas({ project_id: 1 });

Multi-resolution ZIP

POST/api/studio/export_multires
await sf.exports.multires({ project_id: 1, sizes: ['256','512','1024'] });

Project ZIP

POST/api/studio/export_project_zip
await sf.exports.projectZip({ project_id: 1, include_audit: true });

Costs

Cost report

GET/api/studio/costs

USD aggregates by day / provider / project. Use ?since=2026-05-01.

await sf.costs.summary({ since: '2026-05-01' });

Webhooks

Create webhook

POST/api/studio/webhooks

Subscribe to events: asset.approved, project.created, cost.daily_threshold_exceeded, comment.mentioned, etc. Slack/Discord get pre-formatted payloads.

await sf.webhooks.create({
  name: 'Designers Slack',
  url:  'https://hooks.slack.com/services/...',
  kind: 'slack',
  events: ['asset.approved', 'comment.mentioned'],
});

Billing

My subscription

GET/api/billing/me

Current plan, usage vs quota, period bounds, month-to-date provider spend.

await sf.billing.me();

Stripe Checkout

POST/api/billing/checkout

Returns a hosted Stripe URL. Redirect the user to upgrade.

designer · approver · admin
const { url } = await sf.billing.checkout('starter');
location.href = url;