Skip to content

AI SDK examples by template

Every DojoCode project template can use the DojoCode SDK to call real AI models. This page has a complete, copy-paste example for each template: which file to create or replace, and the code to put in it. The examples use different modalities, so together they show all nine.

Each example follows the same rules:

  • It reads the connection with the exact names for its template — nothing to configure and no API key to paste.
  • It starts a generation only when you click (the preview reloads on every save, so a generation on page load would charge you again and again).
  • It handles both answers — 200 (finished) and 202 (still running, polled every 2 seconds) — and shows the error message when something fails.

Or just ask the assistant

Open the AI Chat panel in your project and describe the feature — the assistant writes this code for your template. The examples below are what it produces, ready to read, copy and adapt.

TemplateWhere the code goesExampleModality
React/App.jsxImage generatortext-to-image
React TS/App.tsxPhoto graderimage-to-text
Vue/App.vueMusic composertext-to-music
Vue TS/App.vuePhoto restylerimage-to-image
Svelte/App.svelteAsk anythingtext-to-text
SvelteKit/src/routes/+page.svelteSticker makertext-to-image
SvelteKit TS/src/routes/+page.svelteVideo generatortext-to-video
SolidJS/App.jsxTwo-image generatortext-to-image
SolidJS TS/App.tsxQuiz generator (JSON)text-to-text
Vanilla JS/index.jsVoice notes to textspeech-to-text
Vanilla TS/index.tsNarrator with a cloned voicetext-to-speech
Remix/app/routes/_index.jsxPoster generatortext-to-image
Remix TS/app/routes/_index.tsxPhoto animatorimage-to-video
React Native/App.jsxWallpaper generatortext-to-image
React Native TS/App.tsxCoding tutortext-to-text
Next.js/src/app/page.jsxVideo generatortext-to-video
Next.js TS/src/app/page.tsxMusic composertext-to-music
Astro/src/pages/index.astroStory writertext-to-text
Astro TS/src/pages/index.astroImage generatortext-to-image
Angular/app.component.tsImage generatortext-to-image
Fastify/src/app.tsGET /fact routetext-to-text
Hono/src/app.tsGET /poster routetext-to-image
NestJS/src/app.service.tsPOST /summarize routetext-to-text
Node.js/main.jsFun facttext-to-text
Node.js TS/main.tsFun facttext-to-text
Python — browser preview/main.py + /index.htmlPostcard generatortext-to-image
Python — Run/main.pyImage with pollingtext-to-image
Java/Main.javaFun facttext-to-text
Go/main.goFun facttext-to-text
C#/Main.csFun facttext-to-text
PHP/main.phpFun facttext-to-text
Ruby/main.rbFun facttext-to-text
Rust/main.rsFun facttext-to-text

C and C++

C and C++ projects can't call the SDK yet: their Run environment has no built-in HTTPS client. Use one of the templates above for AI features.

Browser templates (Vite)

React, Vue, Svelte, SvelteKit, SolidJS, Vanilla, Remix and React Native all run on Vite and read the connection from import.meta.env.VITE_DOJOCODE_API_URL and import.meta.env.VITE_DOJOCODE_AI_TOKEN.

The helper for Vite templates

Save the helper once next to your components — /dojocode-ai.js in the JavaScript templates, /dojocode-ai.ts in the TypeScript ones (in SvelteKit put it in /src/lib/, in Remix in /app/). The examples below import it.

js
const API_URL = import.meta.env.VITE_DOJOCODE_API_URL;
const API_TOKEN = import.meta.env.VITE_DOJOCODE_AI_TOKEN;
const FINISHED = ['succeeded', 'failed', 'cancelled'];

async function request(path, options = {}) {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data;
}

/** Submit a generation and resolve with its output (handles 200 and 202). */
export async function generate(modality, input) {
  let record = await request('/ai-generation/generations', {
    method: 'POST',
    body: JSON.stringify({ modality, input })
  });
  while (!FINISHED.includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output;
}

/** Downscale a picked image to at most 1024 px and return a JPEG data URI. */
export function imageToDataUri(file, maxSide = 1024) {
  return new Promise((resolve, reject) => {
    const img = document.createElement('img');
    img.onload = () => {
      const scale = Math.min(1, maxSide / Math.max(img.width, img.height));
      const canvas = document.createElement('canvas');
      canvas.width = Math.round(img.width * scale);
      canvas.height = Math.round(img.height * scale);
      canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
      URL.revokeObjectURL(img.src);
      resolve(canvas.toDataURL('image/jpeg', 0.8));
    };
    img.onerror = reject;
    img.src = URL.createObjectURL(file);
  });
}

/** Turn a Blob or File (audio, image) into a data URI. */
export function blobToDataUri(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}
ts
const API_URL = import.meta.env.VITE_DOJOCODE_API_URL as string;
const API_TOKEN = import.meta.env.VITE_DOJOCODE_AI_TOKEN as string;

export type Modality =
  | 'text-to-text'
  | 'image-to-text'
  | 'text-to-image'
  | 'image-to-image'
  | 'text-to-video'
  | 'image-to-video'
  | 'text-to-music'
  | 'text-to-speech'
  | 'speech-to-text';

interface GenerationRecord {
  id: string;
  status: 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
  output: unknown;
  error: { message: string } | null;
}

async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data as GenerationRecord;
}

/** Submit a generation and resolve with its output (handles 200 and 202). */
export async function generate<T = unknown>(modality: Modality, input: Record<string, unknown>): Promise<T> {
  let record = await request('/ai-generation/generations', {
    method: 'POST',
    body: JSON.stringify({ modality, input })
  });
  while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output as T;
}

/** Downscale a picked image to at most 1024 px and return a JPEG data URI. */
export function imageToDataUri(file: File, maxSide = 1024): Promise<string> {
  return new Promise((resolve, reject) => {
    const img = document.createElement('img');
    img.onload = () => {
      const scale = Math.min(1, maxSide / Math.max(img.width, img.height));
      const canvas = document.createElement('canvas');
      canvas.width = Math.round(img.width * scale);
      canvas.height = Math.round(img.height * scale);
      canvas.getContext('2d')?.drawImage(img, 0, 0, canvas.width, canvas.height);
      URL.revokeObjectURL(img.src);
      resolve(canvas.toDataURL('image/jpeg', 0.8));
    };
    img.onerror = reject;
    img.src = URL.createObjectURL(file);
  });
}

/** Turn a Blob or File (audio, image) into a data URI. */
export function blobToDataUri(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

React

Replace /App.jsx — an image generator (text-to-image, 3 AI tokens per image):

jsx
import { useState } from 'react';
import { generate } from './dojocode-ai';

export default function App() {
  const [prompt, setPrompt] = useState('');
  const [images, setImages] = useState([]);
  const [status, setStatus] = useState('');
  const [busy, setBusy] = useState(false);

  async function handleGenerate() {
    setBusy(true);
    setStatus('Generating…');
    try {
      setImages(await generate('text-to-image', { prompt, aspect_ratio: '16:9' }));
      setStatus('');
    } catch (error) {
      setStatus(error.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <main style={{ maxWidth: 640, margin: '2rem auto', fontFamily: 'sans-serif' }}>
      <h1>Image generator</h1>
      <input value={prompt} onChange={(event) => setPrompt(event.target.value)} placeholder="A lighthouse in a storm, oil painting" style={{ width: '100%' }} />
      <button onClick={handleGenerate} disabled={busy || !prompt.trim()}>
        Generate (3 AI tokens)
      </button>
      <p>{status}</p>
      {images.map((url) => (
        <img key={url} src={url} alt={prompt} style={{ width: '100%', borderRadius: 8 }} />
      ))}
    </main>
  );
}

React TS

Replace /App.tsx — a photo grader (image-to-text, 2 AI tokens):

tsx
import { useState, type ChangeEvent } from 'react';
import { generate, imageToDataUri } from './dojocode-ai';

export default function App() {
  const [image, setImage] = useState<string | null>(null);
  const [answer, setAnswer] = useState('');
  const [busy, setBusy] = useState(false);

  async function handleFile(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    if (!file) return;
    setImage(await imageToDataUri(file));
    setAnswer('');
  }

  async function handleGrade() {
    if (!image) return;
    setBusy(true);
    setAnswer('Looking at your photo…');
    try {
      setAnswer(
        await generate<string>('image-to-text', {
          prompt: 'Grade this photo from 1 to 10, then give three short tips to improve it.',
          image
        })
      );
    } catch (error) {
      setAnswer((error as Error).message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <main style={{ maxWidth: 640, margin: '2rem auto', fontFamily: 'sans-serif' }}>
      <h1>Photo grader</h1>
      <input type="file" accept="image/*" onChange={handleFile} />
      {image && <img src={image} alt="Your photo" style={{ width: '100%', marginTop: 12 }} />}
      <button onClick={handleGrade} disabled={!image || busy}>
        Grade my photo (2 AI tokens)
      </button>
      <p style={{ whiteSpace: 'pre-wrap' }}>{answer}</p>
    </main>
  );
}

Vue

Replace /App.vue — a music composer (text-to-music, 4 AI tokens per 10 seconds):

vue
<script setup>
import { ref } from 'vue';
import { generate } from './dojocode-ai';

const prompt = ref('Upbeat lo-fi hip hop with warm piano chords and a relaxed drum groove');
const duration = ref(8);
const audioUrl = ref('');
const status = ref('');
const busy = ref(false);

async function compose() {
  busy.value = true;
  status.value = 'Composing — this takes about a minute…';
  try {
    audioUrl.value = await generate('text-to-music', { prompt: prompt.value, duration: duration.value, output_format: 'mp3' });
    status.value = '';
  } catch (error) {
    status.value = error.message;
  } finally {
    busy.value = false;
  }
}
</script>

<template>
  <main>
    <h1>Music composer</h1>
    <textarea v-model="prompt" rows="3"></textarea>
    <select v-model.number="duration">
      <option :value="8">8 seconds (4 AI tokens)</option>
      <option :value="15">15 seconds (8 AI tokens)</option>
      <option :value="30">30 seconds (12 AI tokens)</option>
    </select>
    <button :disabled="busy || !prompt.trim()" @click="compose">Compose</button>
    <p>{{ status }}</p>
    <audio v-if="audioUrl" :src="audioUrl" controls></audio>
  </main>
</template>

Vue TS

Replace /App.vue — a photo restyler (image-to-image, 5 AI tokens per image):

vue
<script setup lang="ts">
import { ref } from 'vue';
import { generate, imageToDataUri } from './dojocode-ai';

const source = ref<string | null>(null);
const prompt = ref('Turn this photo into a watercolor painting');
const results = ref<string[]>([]);
const status = ref('');

async function pick(event: Event) {
  const file = (event.target as HTMLInputElement).files?.[0];
  if (file) source.value = await imageToDataUri(file);
}

async function restyle() {
  if (!source.value) return;
  status.value = 'Painting…';
  try {
    results.value = await generate<string[]>('image-to-image', {
      prompt: prompt.value,
      image: source.value,
      prompt_strength: 0.6
    });
    status.value = '';
  } catch (error) {
    status.value = (error as Error).message;
  }
}
</script>

<template>
  <main>
    <h1>Photo restyler</h1>
    <input type="file" accept="image/*" @change="pick" />
    <input v-model="prompt" />
    <button :disabled="!source" @click="restyle">Restyle (5 AI tokens)</button>
    <p>{{ status }}</p>
    <img v-for="url in results" :key="url" :src="url" width="480" />
  </main>
</template>

Svelte

Replace /App.svelte — ask anything (text-to-text, 1 AI token):

svelte
<script>
  import { generate } from './dojocode-ai';

  let prompt = '';
  let answer = '';
  let busy = false;

  async function ask() {
    busy = true;
    answer = 'Thinking…';
    try {
      answer = await generate('text-to-text', { prompt, max_tokens: 400 });
    } catch (error) {
      answer = error.message;
    } finally {
      busy = false;
    }
  }
</script>

<main>
  <h1>Ask anything</h1>
  <textarea bind:value={prompt} rows="4" placeholder="Explain recursion like I'm ten"></textarea>
  <button on:click={ask} disabled={busy || !prompt.trim()}>Ask (1 AI token)</button>
  <p style="white-space: pre-wrap">{answer}</p>
</main>

SvelteKit

Save the helper as /src/lib/dojocode-ai.js, then replace /src/routes/+page.svelte — a sticker maker (text-to-image, 3 AI tokens per image):

svelte
<script>
  import { generate } from '$lib/dojocode-ai';

  let subject = '';
  let stickers = [];
  let status = '';

  async function makeStickers() {
    status = 'Drawing 2 stickers…';
    try {
      stickers = await generate('text-to-image', {
        prompt: `A cute die-cut sticker of ${subject}, thick white border, flat colors`,
        num_outputs: 2,
        aspect_ratio: '1:1'
      });
      status = '';
    } catch (error) {
      status = error.message;
    }
  }
</script>

<h1>Sticker maker</h1>
<input bind:value={subject} placeholder="a sleepy cat astronaut" />
<button on:click={makeStickers} disabled={!subject.trim()}>Make 2 stickers (6 AI tokens)</button>
<p>{status}</p>
{#each stickers as url}
  <img src={url} alt={subject} width="240" />
{/each}

SvelteKit TS

Save the helper as /src/lib/dojocode-ai.ts, then replace /src/routes/+page.svelte — a video generator (text-to-video, 12 AI tokens per ~4 seconds):

svelte
<script lang="ts">
  import { generate } from '$lib/dojocode-ai';

  let prompt = '';
  let videoUrl = '';
  let status = '';

  async function render() {
    status = 'Rendering — this takes a minute or two…';
    try {
      const [url] = await generate<string[]>('text-to-video', { prompt, aspect_ratio: '16:9' });
      videoUrl = url;
      status = '';
    } catch (error) {
      status = (error as Error).message;
    }
  }
</script>

<h1>Video generator</h1>
<textarea bind:value={prompt} rows="4" placeholder="Describe the scene, the motion, the camera and the light"></textarea>
<button on:click={render} disabled={!prompt.trim()}>Generate video (12 AI tokens)</button>
<p>{status}</p>
{#if videoUrl}
  <video src={videoUrl} controls width="640"></video>
{/if}

SolidJS

Replace /App.jsx — two images at once (text-to-image, 3 AI tokens per image):

jsx
import { createSignal, For } from 'solid-js';
import { generate } from './dojocode-ai';

export default function App() {
  const [prompt, setPrompt] = createSignal('');
  const [images, setImages] = createSignal([]);
  const [status, setStatus] = createSignal('');

  async function handleGenerate() {
    setStatus('Generating 2 images…');
    try {
      setImages(await generate('text-to-image', { prompt: prompt(), num_outputs: 2 }));
      setStatus('');
    } catch (error) {
      setStatus(error.message);
    }
  }

  return (
    <main>
      <h1>Two-image generator</h1>
      <input value={prompt()} onInput={(event) => setPrompt(event.currentTarget.value)} placeholder="A cozy cabin in the snow" />
      <button onClick={handleGenerate} disabled={!prompt().trim()}>
        Generate 2 images (6 AI tokens)
      </button>
      <p>{status()}</p>
      <For each={images()}>{(url) => <img src={url} width={320} />}</For>
    </main>
  );
}

SolidJS TS

Replace /App.tsx — a quiz generator that asks for JSON (text-to-text, 1 AI token):

tsx
import { createSignal, For, Show } from 'solid-js';
import { generate } from './dojocode-ai';

interface Question {
  question: string;
  options: string[];
  answer: number;
}

/** The model may wrap JSON in a code fence or add text around it. */
function parseQuiz(text: string): Question[] {
  const start = text.indexOf('[');
  const end = text.lastIndexOf(']');
  try {
    const data = JSON.parse(text.slice(start, end + 1));
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

export default function App() {
  const [topic, setTopic] = createSignal('');
  const [quiz, setQuiz] = createSignal<Question[]>([]);
  const [status, setStatus] = createSignal('');

  async function makeQuiz() {
    setStatus('Writing the quiz…');
    try {
      const text = await generate<string>('text-to-text', {
        prompt: `Write 3 multiple-choice questions about ${topic()}. Reply ONLY with a JSON array of objects with "question", "options" (4 strings) and "answer" (the index of the correct option).`,
        max_tokens: 800,
        temperature: 0.4
      });
      const questions = parseQuiz(text);
      setQuiz(questions);
      setStatus(questions.length ? '' : 'The answer was not valid JSON — try again.');
    } catch (error) {
      setStatus((error as Error).message);
    }
  }

  return (
    <main>
      <h1>Quiz generator</h1>
      <input value={topic()} onInput={(event) => setTopic(event.currentTarget.value)} placeholder="the solar system" />
      <button onClick={makeQuiz} disabled={!topic().trim()}>Make a quiz (1 AI token)</button>
      <p>{status()}</p>
      <For each={quiz()}>
        {(item) => (
          <section>
            <h3>{item.question}</h3>
            <ol type="A">
              <For each={item.options}>{(option, index) => <li>{option}{index() === item.answer ? ' ✓' : ''}</li>}</For>
            </ol>
          </section>
        )}
      </For>
      <Show when={!quiz().length && !status()}>
        <p>Pick a topic to start.</p>
      </Show>
    </main>
  );
}

Vanilla JS

Replace /index.js — voice notes to text (speech-to-text, 2 AI tokens per recording). The page is built from JavaScript, so no HTML file is needed:

js
import { generate, blobToDataUri } from './dojocode-ai.js';

document.body.innerHTML = `
  <main style="max-width: 640px; margin: 2rem auto; font-family: sans-serif">
    <h1>Voice notes to text</h1>
    <input type="file" id="recording" accept="audio/*">
    <button id="transcribe">Transcribe (2 AI tokens)</button>
    <pre id="result" style="white-space: pre-wrap"></pre>
  </main>
`;

const button = document.querySelector('#transcribe');
const result = document.querySelector('#result');

button.addEventListener('click', async () => {
  const file = document.querySelector('#recording').files[0];
  if (!file) {
    result.textContent = 'Pick an audio file first.';
    return;
  }
  button.disabled = true;
  result.textContent = 'Transcribing…';
  try {
    const { transcription, detected_language } = await generate('speech-to-text', {
      audio: await blobToDataUri(file),
      language: 'auto'
    });
    result.textContent = `[${detected_language}] ${transcription}`;
  } catch (error) {
    result.textContent = error.message;
  } finally {
    button.disabled = false;
  }
});

Vanilla TS

Replace /index.ts — a narrator that speaks in a cloned voice (text-to-speech, 3 AI tokens per 1,000 characters):

ts
import { generate, blobToDataUri } from './dojocode-ai';

document.body.innerHTML = `
  <main style="max-width: 640px; margin: 2rem auto; font-family: sans-serif">
    <h1>Narrator</h1>
    <p><label>Voice sample (10–30 s): <input type="file" id="sample" accept="audio/*"></label></p>
    <p><label>What the sample says: <input id="sampleText" style="width: 100%"></label></p>
    <textarea id="text" rows="4" style="width: 100%" placeholder="Text to read aloud"></textarea>
    <button id="speak">Speak (3 AI tokens per 1,000 characters)</button>
    <p id="status"></p>
    <audio id="player" controls hidden></audio>
  </main>
`;

const status = document.querySelector<HTMLParagraphElement>('#status')!;
const player = document.querySelector<HTMLAudioElement>('#player')!;

document.querySelector('#speak')!.addEventListener('click', async () => {
  const sample = document.querySelector<HTMLInputElement>('#sample')!.files?.[0];
  const sampleText = document.querySelector<HTMLInputElement>('#sampleText')!.value;
  const text = document.querySelector<HTMLTextAreaElement>('#text')!.value;
  if (!sample || !sampleText.trim() || !text.trim()) {
    status.textContent = 'Add a voice sample, its exact words and the text to read.';
    return;
  }
  status.textContent = 'Generating the voice — this can take a minute…';
  try {
    player.src = await generate<string>('text-to-speech', {
      text,
      speaker_reference: await blobToDataUri(sample),
      text_reference: sampleText
    });
    player.hidden = false;
    status.textContent = '';
  } catch (error) {
    status.textContent = (error as Error).message;
  }
});

Remix

Save the helper as /app/dojocode-ai.js, then replace /app/routes/_index.jsx — a poster generator (text-to-image, 3 AI tokens per image):

jsx
import { useState } from 'react';
import { generate } from '../dojocode-ai';

export default function Index() {
  const [title, setTitle] = useState('');
  const [poster, setPoster] = useState('');
  const [status, setStatus] = useState('');

  async function handleGenerate() {
    setStatus('Designing your poster…');
    try {
      const [url] = await generate('text-to-image', {
        prompt: `A bold retro travel poster for "${title}", screen-print style, strong typography`,
        aspect_ratio: '2:3'
      });
      setPoster(url);
      setStatus('');
    } catch (error) {
      setStatus(error.message);
    }
  }

  return (
    <main>
      <h1>Poster generator</h1>
      <input value={title} onChange={(event) => setTitle(event.target.value)} placeholder="Visit Mars" />
      <button onClick={handleGenerate} disabled={!title.trim()}>
        Generate (3 AI tokens)
      </button>
      <p>{status}</p>
      {poster && <img src={poster} alt={title} width={360} />}
    </main>
  );
}

Remix TS

Save the helper as /app/dojocode-ai.ts, then replace /app/routes/_index.tsx — a photo animator (image-to-video, 12 AI tokens per ~4 seconds):

tsx
import { useState, type ChangeEvent } from 'react';
import { generate, imageToDataUri } from '../dojocode-ai';

export default function Index() {
  const [image, setImage] = useState<string | null>(null);
  const [prompt, setPrompt] = useState('The scene slowly comes alive, gentle camera push-in, soft wind');
  const [videoUrl, setVideoUrl] = useState('');
  const [status, setStatus] = useState('');

  async function handleFile(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    if (file) setImage(await imageToDataUri(file));
  }

  async function animate() {
    if (!image) return;
    setStatus('Animating — this takes a minute or two…');
    try {
      const [url] = await generate<string[]>('image-to-video', { prompt, image });
      setVideoUrl(url);
      setStatus('');
    } catch (error) {
      setStatus((error as Error).message);
    }
  }

  return (
    <main>
      <h1>Photo animator</h1>
      <input type="file" accept="image/*" onChange={handleFile} />
      <textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} rows={3} />
      <button onClick={animate} disabled={!image}>
        Animate (12 AI tokens)
      </button>
      <p>{status}</p>
      {videoUrl && <video src={videoUrl} controls width={480} />}
    </main>
  );
}

React Native

Replace /App.jsx — a phone wallpaper generator (text-to-image, 3 AI tokens):

jsx
import { useState } from 'react';
import { View, Text, TextInput, Pressable, Image, StyleSheet } from 'react-native';
import { generate } from './dojocode-ai';

export default function App() {
  const [prompt, setPrompt] = useState('');
  const [imageUrl, setImageUrl] = useState(null);
  const [status, setStatus] = useState('');

  async function handleGenerate() {
    setStatus('Generating…');
    try {
      const [url] = await generate('text-to-image', { prompt, aspect_ratio: '9:16' });
      setImageUrl(url);
      setStatus('');
    } catch (error) {
      setStatus(error.message);
    }
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Wallpaper generator</Text>
      <TextInput value={prompt} onChangeText={setPrompt} placeholder="Neon city at night" style={styles.input} />
      <Pressable onPress={handleGenerate} style={styles.button}>
        <Text style={styles.buttonText}>Generate (3 AI tokens)</Text>
      </Pressable>
      <Text>{status}</Text>
      {imageUrl && <Image source={{ uri: imageUrl }} style={styles.wallpaper} />}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', padding: 24, gap: 12 },
  title: { fontSize: 24, fontWeight: '600' },
  input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 10, width: 280 },
  button: { backgroundColor: '#6c5ce7', borderRadius: 8, paddingVertical: 12, paddingHorizontal: 20 },
  buttonText: { color: 'white', fontWeight: '600' },
  wallpaper: { width: 216, height: 384, borderRadius: 12 }
});

React Native and images

Image from 'react-native' shadows the browser's Image, so the helper creates image elements with document.createElement('img'). Never use new Image() in React Native code.

React Native TS

Replace /App.tsx — a coding tutor (text-to-text, 1 AI token):

tsx
import { useState } from 'react';
import { ScrollView, Text, TextInput, Pressable, StyleSheet } from 'react-native';
import { generate } from './dojocode-ai';

export default function App() {
  const [question, setQuestion] = useState('');
  const [answer, setAnswer] = useState('');
  const [busy, setBusy] = useState(false);

  async function ask() {
    setBusy(true);
    setAnswer('Thinking…');
    try {
      setAnswer(
        await generate<string>('text-to-text', {
          prompt: question,
          system_prompt: 'You are a patient coding tutor. Answer in at most five sentences with one small example.',
          max_tokens: 400
        })
      );
    } catch (error) {
      setAnswer((error as Error).message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <ScrollView contentContainerStyle={styles.container}>
      <Text style={styles.title}>Coding tutor</Text>
      <TextInput value={question} onChangeText={setQuestion} placeholder="What is a closure?" multiline style={styles.input} />
      <Pressable onPress={ask} disabled={busy || !question.trim()} style={styles.button}>
        <Text style={styles.buttonText}>Ask (1 AI token)</Text>
      </Pressable>
      <Text style={styles.answer}>{answer}</Text>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: { padding: 24, gap: 12 },
  title: { fontSize: 24, fontWeight: '600' },
  input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 10, minHeight: 80 },
  button: { backgroundColor: '#6c5ce7', borderRadius: 8, padding: 12, alignItems: 'center' },
  buttonText: { color: 'white', fontWeight: '600' },
  answer: { fontSize: 16, lineHeight: 22 }
});

Next.js templates

Only NEXT_PUBLIC_ variables reach browser code, and import.meta.env doesn't exist in Next.js. Call the SDK from a Client Component ('use client').

Next.js

Create /src/lib/dojocode-ai.js and replace /src/app/page.jsx — a video generator (text-to-video, 12 AI tokens per ~4 seconds):

js
const API_URL = process.env.NEXT_PUBLIC_DOJOCODE_API_URL;
const API_TOKEN = process.env.NEXT_PUBLIC_DOJOCODE_AI_TOKEN;

async function request(path, options = {}) {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data;
}

export async function generate(modality, input) {
  let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
  while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output;
}
jsx
'use client';

import { useState } from 'react';
import { generate } from '../lib/dojocode-ai';

export default function Page() {
  const [prompt, setPrompt] = useState('');
  const [videoUrl, setVideoUrl] = useState('');
  const [status, setStatus] = useState('');

  async function handleGenerate() {
    setStatus('Rendering the video — this takes a minute or two…');
    try {
      const [url] = await generate('text-to-video', { prompt, aspect_ratio: '16:9', length: 97 });
      setVideoUrl(url);
      setStatus('');
    } catch (error) {
      setStatus(error.message);
    }
  }

  return (
    <main>
      <h1>Video generator</h1>
      <textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} rows={4} placeholder="A slow drone shot over a misty forest at sunrise" />
      <button onClick={handleGenerate} disabled={!prompt.trim()}>
        Generate video (12 AI tokens)
      </button>
      <p>{status}</p>
      {videoUrl && <video src={videoUrl} controls width={640} />}
    </main>
  );
}

Next.js TS

Create /src/lib/dojocode-ai.ts and replace /src/app/page.tsx — a music composer (text-to-music, 4 AI tokens per 10 seconds):

ts
const API_URL = process.env.NEXT_PUBLIC_DOJOCODE_API_URL;
const API_TOKEN = process.env.NEXT_PUBLIC_DOJOCODE_AI_TOKEN;

interface GenerationRecord {
  id: string;
  status: string;
  output: unknown;
  error: { message: string } | null;
}

async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data as GenerationRecord;
}

export async function generate<T = unknown>(modality: string, input: Record<string, unknown>): Promise<T> {
  let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
  while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output as T;
}
tsx
'use client';

import { useState } from 'react';
import { generate } from '../lib/dojocode-ai';

export default function Page() {
  const [prompt, setPrompt] = useState('Epic orchestral trailer music with big drums and strings');
  const [duration, setDuration] = useState(15);
  const [audioUrl, setAudioUrl] = useState('');
  const [status, setStatus] = useState('');

  async function compose() {
    setStatus('Composing…');
    try {
      setAudioUrl(await generate<string>('text-to-music', { prompt, duration, output_format: 'mp3' }));
      setStatus('');
    } catch (error) {
      setStatus((error as Error).message);
    }
  }

  return (
    <main>
      <h1>Music composer</h1>
      <textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} rows={3} />
      <select value={duration} onChange={(event) => setDuration(Number(event.target.value))}>
        <option value={8}>8 seconds (4 AI tokens)</option>
        <option value={15}>15 seconds (8 AI tokens)</option>
        <option value={30}>30 seconds (12 AI tokens)</option>
      </select>
      <button onClick={compose}>Compose</button>
      <p>{status}</p>
      {audioUrl && <audio src={audioUrl} controls />}
    </main>
  );
}

Astro templates

Astro exposes PUBLIC_ variables to browser <script> code (the VITE_ names are undefined there). The page's <script> runs in the browser and can import the helper.

Astro

Create /src/lib/dojocode-ai.js and replace /src/pages/index.astro — a story writer (text-to-text, 1 AI token):

js
const API_URL = import.meta.env.PUBLIC_DOJOCODE_API_URL;
const API_TOKEN = import.meta.env.PUBLIC_DOJOCODE_AI_TOKEN;

async function request(path, options = {}) {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data;
}

export async function generate(modality, input) {
  let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
  while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output;
}
astro
---
const title = 'Story writer';
---

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>{title}</title>
  </head>
  <body>
    <h1>{title}</h1>
    <input id="topic" placeholder="a dragon who is afraid of the dark" />
    <button id="write">Write a story (1 AI token)</button>
    <p id="story" style="white-space: pre-wrap"></p>

    <script>
      import { generate } from '../lib/dojocode-ai.js';

      const topic = document.querySelector('#topic');
      const story = document.querySelector('#story');

      document.querySelector('#write').addEventListener('click', async () => {
        story.textContent = 'Writing…';
        try {
          story.textContent = await generate('text-to-text', {
            prompt: `Write a short bedtime story for kids about ${topic.value}.`,
            max_tokens: 600
          });
        } catch (error) {
          story.textContent = error.message;
        }
      });
    </script>
  </body>
</html>

Astro TS

Create /src/lib/dojocode-ai.ts (the Next.js TS helper above, with import.meta.env.PUBLIC_DOJOCODE_API_URL and import.meta.env.PUBLIC_DOJOCODE_AI_TOKEN in its first two lines) and replace /src/pages/index.astro — an image generator (text-to-image, 3 AI tokens):

ts
const API_URL = import.meta.env.PUBLIC_DOJOCODE_API_URL as string;
const API_TOKEN = import.meta.env.PUBLIC_DOJOCODE_AI_TOKEN as string;

interface GenerationRecord {
  id: string;
  status: string;
  output: unknown;
  error: { message: string } | null;
}

async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data as GenerationRecord;
}

export async function generate<T = unknown>(modality: string, input: Record<string, unknown>): Promise<T> {
  let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
  while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output as T;
}
astro
---
const title = 'Image generator';
---

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>{title}</title>
  </head>
  <body>
    <h1>{title}</h1>
    <input id="prompt" placeholder="A koi pond in autumn, ukiyo-e print" />
    <button id="generate">Generate (3 AI tokens)</button>
    <p id="status"></p>
    <img id="result" width="480" hidden />

    <script>
      import { generate } from '../lib/dojocode-ai';

      const prompt = document.querySelector('#prompt') as HTMLInputElement;
      const status = document.querySelector('#status') as HTMLParagraphElement;
      const result = document.querySelector('#result') as HTMLImageElement;

      document.querySelector('#generate')?.addEventListener('click', async () => {
        status.textContent = 'Generating…';
        try {
          const [url] = await generate<string[]>('text-to-image', { prompt: prompt.value });
          result.src = url;
          result.hidden = false;
          status.textContent = '';
        } catch (error) {
          status.textContent = (error as Error).message;
        }
      });
    </script>
  </body>
</html>

Angular template

The platform adds dojocode-env.ts next to app.component.ts — it is hidden from the file tree, so just import it. process.env, import.meta.env and window globals are not available in Angular. FormsModule is already imported in app.module.ts, so [(ngModel)] works out of the box.

Angular

Create /dojocode-ai.ts and replace /app.component.ts and /app.component.html — an image generator (text-to-image, 3 AI tokens):

ts
import { DOJOCODE_ENV } from './dojocode-env';

const API_URL = DOJOCODE_ENV['DOJOCODE_API_URL'];
const API_TOKEN = DOJOCODE_ENV['DOJOCODE_AI_TOKEN'];

interface GenerationRecord {
  id: string;
  status: string;
  output: unknown;
  error: { message: string } | null;
}

async function request(path: string, options: RequestInit = {}): Promise<GenerationRecord> {
  const response = await fetch(`${API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data as GenerationRecord;
}

export async function generate<T = unknown>(modality: string, input: Record<string, unknown>): Promise<T> {
  let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
  while (!['succeeded', 'failed', 'cancelled'].includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output as T;
}
ts
import { Component } from '@angular/core';
import { generate } from './dojocode-ai';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: []
})
export class AppComponent {
  prompt = '';
  images: string[] = [];
  status = '';
  busy = false;

  async generateImages(): Promise<void> {
    this.busy = true;
    this.status = 'Generating…';
    try {
      this.images = await generate<string[]>('text-to-image', { prompt: this.prompt });
      this.status = '';
    } catch (error) {
      this.status = (error as Error).message;
    } finally {
      this.busy = false;
    }
  }
}
html
<main>
  <h1>Image generator</h1>
  <input [(ngModel)]="prompt" placeholder="A paper boat on a blue pond" />
  <button (click)="generateImages()" [disabled]="busy || !prompt.trim()">Generate (3 AI tokens)</button>
  <p>{{ status }}</p>
  <img *ngFor="let url of images" [src]="url" width="480" />
</main>

Node.js servers in the preview

NestJS, Fastify and Hono run in the preview with the connection in process.env.DOJOCODE_API_URL and process.env.DOJOCODE_AI_TOKEN. They call the SDK with the built-in fetch — no HTTP library needed. Save this helper as /src/dojocode-ai.ts and test the routes from the preview or the API Tester:

ts
// src/dojocode-ai.ts
const FINISHED = ['succeeded', 'failed', 'cancelled'];

async function request(path: string, options: RequestInit = {}): Promise<any> {
  const response = await fetch(`${process.env.DOJOCODE_API_URL}${path}`, {
    ...options,
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.DOJOCODE_AI_TOKEN}` }
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) throw new Error(data?.errors?.[0]?.message ?? `Request failed (${response.status})`);
  return data;
}

/** Submit a generation and resolve with its output (handles 200 and 202). */
export async function generate(modality: string, input: Record<string, unknown>): Promise<unknown> {
  let record = await request('/ai-generation/generations', { method: 'POST', body: JSON.stringify({ modality, input }) });
  while (!FINISHED.includes(record.status)) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    record = await request(`/ai-generation/generations/${record.id}`);
  }
  if (record.status !== 'succeeded') throw new Error(record.error?.message ?? 'The generation failed.');
  return record.output;
}

Fastify

Replace /src/app.ts (main.ts keeps calling buildApp()) — GET /fact?topic=octopus (text-to-text, 1 AI token per call):

ts
import Fastify, { type FastifyInstance } from 'fastify';
import { generate } from './dojocode-ai';

export function buildApp(): FastifyInstance {
  const app = Fastify({ logger: false });

  app.get('/', async () => 'Hello, World! Try GET /fact?topic=octopus');

  app.get<{ Querystring: { topic?: string } }>('/fact', async (request, reply) => {
    try {
      const fact = await generate('text-to-text', {
        prompt: `Write one surprising fact about ${request.query.topic ?? 'space'}.`,
        max_tokens: 120
      });
      return { fact };
    } catch (error) {
      return reply.status(502).send({ error: (error as Error).message });
    }
  });

  return app;
}

Hono

Replace /src/app.ts (main.ts keeps serving the exported app) — GET /poster?prompt=retro+robot (text-to-image, 3 AI tokens per call):

ts
import { Hono } from 'hono';
import { generate } from './dojocode-ai';

export const app = new Hono();

app.get('/', (c) => c.text('Hello, World! Try GET /poster?prompt=retro+robot'));

app.get('/poster', async (c) => {
  try {
    const images = await generate('text-to-image', { prompt: c.req.query('prompt') ?? 'a retro robot poster' });
    return c.json({ images });
  } catch (error) {
    return c.json({ error: (error as Error).message }, 502);
  }
});

NestJS

Replace /src/app.service.ts and /src/app.controller.tsPOST /summarize with { "text": "…" } (text-to-text, 1 AI token per call):

ts
import { BadGatewayException, Injectable } from '@nestjs/common';
import { generate } from './dojocode-ai';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello, World! Try POST /summarize';
  }

  async summarize(text: string): Promise<{ summary: unknown }> {
    try {
      const summary = await generate('text-to-text', {
        prompt: `Summarize in three bullet points:\n\n${text}`,
        max_tokens: 250
      });
      return { summary };
    } catch (error) {
      throw new BadGatewayException((error as Error).message);
    }
  }
}
ts
import { Body, Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Post('summarize')
  summarize(@Body('text') text: string) {
    return this.appService.summarize(text);
  }
}

Node.js templates

The Node.js templates read the same process.env variables whether the project runs in the preview or with Run (where a run stops after 20 seconds — see Python — Run for slow modalities).

Node.js

Replace /main.js (text-to-text, 1 AI token):

js
async function main() {
  const response = await fetch(`${process.env.DOJOCODE_API_URL}/ai-generation/generations`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.DOJOCODE_AI_TOKEN}` },
    body: JSON.stringify({
      modality: 'text-to-text',
      input: { prompt: 'Write a one-sentence fun fact about Node.js.', max_tokens: 80 }
    }),
    signal: AbortSignal.timeout(15000)
  });
  const record = await response.json();
  if (!response.ok) {
    console.log('Generation failed: ' + record.errors[0].message);
    return;
  }
  console.log(record.status, String(record.output).trim());
}

main().catch((error) => console.log('Generation failed: ' + error.message));

Node.js TS

Replace /main.ts (text-to-text, 1 AI token):

ts
interface GenerationRecord {
  status?: string;
  output?: unknown;
  errors?: Array<{ message: string }>;
}

async function main(): Promise<void> {
  const response = await fetch(`${process.env.DOJOCODE_API_URL}/ai-generation/generations`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.DOJOCODE_AI_TOKEN}` },
    body: JSON.stringify({
      modality: 'text-to-text',
      input: { prompt: 'Write a one-sentence fun fact about TypeScript.', max_tokens: 80 }
    }),
    signal: AbortSignal.timeout(15000)
  });
  const record = (await response.json()) as GenerationRecord;
  if (!response.ok) {
    console.log('Generation failed: ' + record.errors?.[0]?.message);
    return;
  }
  console.log(record.status, String(record.output).trim());
}

main().catch((error: Error) => console.log('Generation failed: ' + error.message));

Python

Python — browser preview

Python projects with the browser preview run in the browser, and the platform installs a ready-made dojocode_ai module — no HTTP code needed. Create /index.html (the page the preview shows) and replace /main.py (it wires up the page) — a postcard generator (text-to-image, 3 AI tokens):

python
from js import document
from pyodide.ffi import create_proxy
from dojocode_ai import generate_async, DojoCodeAiError


async def on_generate(event):
    result = document.getElementById("result")
    result.textContent = "Painting your postcard..."
    try:
        # Waits until the generation finishes; the page stays responsive meanwhile.
        record = await generate_async(
            "text-to-image",
            {"prompt": f"A vintage postcard of {document.getElementById('place').value}", "aspect_ratio": "3:2"},
            timeout=150,
        )
        result.innerHTML = f'<img src="{record["output"][0]}" style="max-width: 100%; border-radius: 8px">'
    except DojoCodeAiError as error:
        result.textContent = f"Generation failed: {error}"


document.getElementById("generate").addEventListener("click", create_proxy(on_generate))
html
<main style="max-width: 640px; margin: 2rem auto; font-family: sans-serif">
  <h1>Postcard generator</h1>
  <input id="place" placeholder="Lisbon at sunset" />
  <button id="generate">Generate (3 AI tokens)</button>
  <div id="result"></div>
</main>

Use await generate_async(…) / await get_async(id) in event handlers. The synchronous generate() / get() exist for plain scripts, but they freeze the page until the answer arrives.

Python — Run

Without the browser preview, Run executes /main.py in a container with DOJOCODE_API_URL and DOJOCODE_AI_TOKEN in the environment. A run stops after 20 seconds, so this example submits without waiting, polls for up to ~10 seconds and prints the id if the result isn't ready yet:

python
import json, os, time, urllib.request, urllib.error

API_URL = os.environ["DOJOCODE_API_URL"]
HEADERS = {"Content-Type": "application/json", "Authorization": f"Bearer {os.environ['DOJOCODE_AI_TOKEN']}"}


def api(method, path, body=None):
    request = urllib.request.Request(f"{API_URL}{path}", method=method, headers=HEADERS,
                                     data=json.dumps(body).encode() if body is not None else None)
    try:
        with urllib.request.urlopen(request, timeout=15) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        raise RuntimeError(json.load(error)["errors"][0]["message"]) from None


record = api("POST", "/ai-generation/generations?wait=false",
             {"modality": "text-to-image", "input": {"prompt": "a watercolor fox in the snow"}})
for _ in range(5):
    if record["status"] in ("succeeded", "failed", "cancelled"):
        break
    time.sleep(2)
    record = api("GET", f"/ai-generation/generations/{record['id']}")

if record["status"] == "succeeded":
    print("Image:", record["output"][0])
else:
    print(f"Still {record['status']} — run again to read generation {record['id']}")
    # Next run: api("GET", "/ai-generation/generations?limit=1") returns your latest generation.

Run templates

These templates run your program in a container when you press Run, with DOJOCODE_API_URL, DOJOCODE_AI_TOKEN and DOJOCODE_PROJECT_ID in the environment. The examples use text-to-text, which finishes well inside the 20-second limit; for slow modalities follow the Python — Run pattern (submit with ?wait=false, print the id, read it back on the next run).

Java

Replace /Main.java (text-to-text, 1 AI token):

java
package challenge;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Main {
    public static void main(String[] args) throws Exception {
        String apiUrl = System.getenv("DOJOCODE_API_URL");
        String token = System.getenv("DOJOCODE_AI_TOKEN");

        // Use HTTP/1.1: Java's default HTTP/2 upgrade over plain http is not supported.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        String body = "{\"modality\":\"text-to-text\",\"input\":{\"prompt\":\"Write a one-sentence fun fact about Java.\",\"max_tokens\":80}}";

        HttpRequest request = HttpRequest.newBuilder(URI.create(apiUrl + "/ai-generation/generations"))
            .timeout(Duration.ofSeconds(15))
            .header("Content-Type", "application/json")
            .header("Authorization", "Bearer " + token)
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("HTTP " + response.statusCode());
        System.out.println(response.body()); // the generation record as JSON
    }
}

Go

Replace /main.go (text-to-text, 1 AI token):

go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

func main() {
	payload, _ := json.Marshal(map[string]interface{}{
		"modality": "text-to-text",
		"input":    map[string]interface{}{"prompt": "Write a one-sentence fun fact about Go.", "max_tokens": 80},
	})
	request, _ := http.NewRequest("POST", os.Getenv("DOJOCODE_API_URL")+"/ai-generation/generations", bytes.NewReader(payload))
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("Authorization", "Bearer "+os.Getenv("DOJOCODE_AI_TOKEN"))

	client := &http.Client{Timeout: 15 * time.Second}
	response, err := client.Do(request)
	if err != nil {
		fmt.Println("Generation failed:", err)
		return
	}
	defer response.Body.Close()

	var record map[string]interface{}
	json.NewDecoder(response.Body).Decode(&record)
	if failures, ok := record["errors"].([]interface{}); ok && len(failures) > 0 {
		fmt.Println("Generation failed:", failures[0].(map[string]interface{})["message"])
		return
	}
	fmt.Println(record["status"], record["output"])
}

C#

Replace /Main.cs (text-to-text, 1 AI token):

csharp
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

namespace Challenge
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var apiUrl = Environment.GetEnvironmentVariable("DOJOCODE_API_URL");
            var token = Environment.GetEnvironmentVariable("DOJOCODE_AI_TOKEN");

            var payload = JsonSerializer.Serialize(new
            {
                modality = "text-to-text",
                input = new { prompt = "Write a one-sentence fun fact about C#.", max_tokens = 80 }
            });

            using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(15) };
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            var content = new StringContent(payload, Encoding.UTF8, "application/json");
            var response = client.PostAsync(apiUrl + "/ai-generation/generations", content).Result;

            using var document = JsonDocument.Parse(response.Content.ReadAsStringAsync().Result);
            var record = document.RootElement;
            if (record.TryGetProperty("errors", out var errors))
            {
                Console.WriteLine("Generation failed: " + errors[0].GetProperty("message").GetString());
                return;
            }
            Console.WriteLine(record.GetProperty("status").GetString() + ": " + record.GetProperty("output").GetString()?.Trim());
        }
    }
}

PHP

Replace /main.php (text-to-text, 1 AI token):

php
<?php

$apiUrl = getenv('DOJOCODE_API_URL');
$token = getenv('DOJOCODE_AI_TOKEN');

$context = stream_context_create(['http' => [
    'method' => 'POST',
    'header' => "Content-Type: application/json\r\nAuthorization: Bearer $token\r\n",
    'content' => json_encode([
        'modality' => 'text-to-text',
        'input' => ['prompt' => 'Write a one-sentence fun fact about PHP.', 'max_tokens' => 80],
    ]),
    'timeout' => 15,
    'ignore_errors' => true,
]]);

$record = json_decode(file_get_contents("$apiUrl/ai-generation/generations", false, $context) ?: 'null', true);
if (!is_array($record) || isset($record['errors'])) {
    echo 'Generation failed: ' . ($record['errors'][0]['message'] ?? 'no response') . "\n";
    exit;
}
echo "{$record['status']}: " . trim((string) $record['output']) . "\n";

Ruby

Replace /main.rb (text-to-text, 1 AI token):

ruby
require 'json'
require 'net/http'
require 'uri'

uri = URI("#{ENV['DOJOCODE_API_URL']}/ai-generation/generations")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.read_timeout = 15

request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json',
                                   'Authorization' => "Bearer #{ENV['DOJOCODE_AI_TOKEN']}")
request.body = { modality: 'text-to-text', input: { prompt: 'Write a one-sentence fun fact about Ruby.', max_tokens: 80 } }.to_json

record = JSON.parse(http.request(request).body)
if record['errors']
  puts "Generation failed: #{record['errors'][0]['message']}"
else
  puts "#{record['status']}: #{record['output'].to_s.strip}"
end

Rust

Replace /main.rs (text-to-text, 1 AI token). The standard library has no HTTP client, so this example calls the curl command, which the Rust Run environment includes; for a larger app add a crate such as ureq or reqwest (plus serde_json) from the Dependencies panel:

rust
use std::env;
use std::process::Command;

fn main() {
    let api_url = env::var("DOJOCODE_API_URL").unwrap_or_default();
    let token = env::var("DOJOCODE_AI_TOKEN").unwrap_or_default();

    let payload = r#"{"modality":"text-to-text","input":{"prompt":"Write a one-sentence fun fact about Rust.","max_tokens":80}}"#;
    let output = Command::new("curl")
        .args([
            "-s", "--max-time", "15", "-X", "POST",
            "-H", "Content-Type: application/json",
            "-H", &format!("Authorization: Bearer {}", token),
            "-d", payload,
            &format!("{}/ai-generation/generations", api_url),
        ])
        .output()
        .expect("curl is not available");

    // The generation record as JSON — parse it with serde_json in a real app.
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

What's next?