Skip to content

Examples

curl - end-to-end flow

bash
BASE="https://ai.klikg.com"
KEY="sk-..."

# 1. Create a project
PROJECT_ID=$(curl -s -X POST "$BASE/v1/projects" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Runner Demo", "template": "blank" }' | jq -r '.data._id')

# 2. Submit a job (retry on 503 cold start)
until JOB=$(curl -s -f -X POST "$BASE/v1/generate" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"projectId\": \"$PROJECT_ID\",
    \"prompt\": \"Build a one-button endless runner where you dodge falling blocks.\",
    \"settings\": { \"gameType\": \"klik-short\" }
  }"); do
  echo "engine starting, retrying in 30s..."; sleep 30
done
JOB_ID=$(echo "$JOB" | jq -r '.jobId')

# 3. Poll until terminal
while :; do
  STATUS=$(curl -s "$BASE/v1/generate/$JOB_ID/status?projectId=$PROJECT_ID" \
    -H "Authorization: Bearer $KEY" | jq -r '.status')
  echo "status: $STATUS"
  case "$STATUS" in completed|failed|cancelled) break;; esac
  sleep 10
done

# 4. Download the game
curl -sL -o game.zip \
  "$BASE/v1/generate/$JOB_ID/files?projectId=$PROJECT_ID" \
  -H "Authorization: Bearer $KEY"

Node.js - autonomous creator loop

A complete script using only built-in fetch (Node 18+). It sends the full creation contract on the first job, then a follow-up job that inherits the persisted instructions.

js
const BASE = process.env.KLIK_BASE ?? 'https://ai.klikg.com';
const KEY = process.env.KLIK_API_KEY; // sk-...

const headers = {
  'Authorization': `Bearer ${KEY}`,
  'Content-Type': 'application/json',
};

async function api(method, path, body) {
  const res = await fetch(`${BASE}${path}`, {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!res.ok) {
    const err = new Error(`${method} ${path} -> ${res.status}`);
    err.status = res.status;
    err.body = await res.json().catch(() => null);
    throw err;
  }
  return res.json();
}

// Submit with 503 cold-start retries (up to 8 minutes).
async function submitJob(body) {
  const deadline = Date.now() + 8 * 60_000;
  for (;;) {
    try {
      return await api('POST', '/v1/generate', body);
    } catch (err) {
      if (err.status !== 503 || Date.now() > deadline) throw err;
      const wait = (err.body?.retryAfterSeconds ?? 30) * 1000;
      console.log(`engine starting, retrying in ${wait / 1000}s...`);
      await new Promise(r => setTimeout(r, wait));
    }
  }
}

async function waitForJob(jobId, projectId) {
  for (;;) {
    const job = await api(
      'GET',
      `/v1/generate/${jobId}/status?projectId=${projectId}`
    );
    console.log(`status: ${job.status}`);
    if (['completed', 'failed', 'cancelled'].includes(job.status)) return job;
    await new Promise(r => setTimeout(r, 10_000));
  }
}

// 1. Project
const project = await api('POST', '/v1/projects', {
  name: 'Runner Demo',
  template: 'blank',
});
const projectId = project.data._id;

// 2. First job: full creation contract
const first = await submitJob({
  projectId,
  prompt: 'Build a one-button endless runner where you dodge falling blocks.',
  instructions:
    'Bold flat-color art, punchy game feel, always a 30-60 second short. ' +
    'Be transparent that games are AI-made.',
  context: [
    {
      name: 'research',
      content:
        'Top-performing runners restart within 3 seconds of failure. ' +
        'Difficulty should ramp every 10 seconds.',
    },
  ],
  settings: { gameType: 'klik-short', reasoningLevel: 'medium' },
});
await waitForJob(first.jobId, projectId);

// 3. Follow-up job: instructions persist, so only the change request is needed
const second = await submitJob({
  projectId,
  prompt: 'Tighten the jump gravity and add a combo counter for near-misses.',
  context: [
    { name: 'playtest-feedback', content: 'Players said the jump felt floaty.' },
  ],
});
await waitForJob(second.jobId, projectId);

// 4. Download the ZIP
const zipRes = await fetch(
  `${BASE}/v1/generate/${second.jobId}/files?projectId=${projectId}`,
  { headers }
);
const fs = await import('node:fs/promises');
await fs.writeFile('game.zip', Buffer.from(await zipRes.arrayBuffer()));
console.log('saved game.zip');

Streaming progress (SSE)

Instead of polling, consume the live event stream:

js
const res = await fetch(
  `${BASE}/v1/generate/${jobId}/events?projectId=${projectId}`,
  { headers: { Authorization: `Bearer ${KEY}` } }
);
const reader = res.body.getReader();
const decoder = new TextDecoder();
for (;;) {
  const { done, value } = await reader.read();
  if (done) break;
  process.stdout.write(decoder.decode(value));
}

Events include assistant text, tool calls, build results, and the final completion event.

Klik Engine - AI game creation.