oatmeal
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data β all from the browser, and deployed in milliseconds.
Instructions for AI coding agents working on this Val Town project.
Meeting recording automation platform for Val Town:
- Send bots to meetings via Recall.ai
- Manage recordings with transcripts
- Val Town SQLite database
- Single-file deployment in
val.ts
- All code goes in
val.ts- Single file deployment - Use esm.sh imports:
import { Hono } from "https://esm.sh/hono@4" - Val Town SQLite:
import { sqlite } from "https://esm.town/v/std/sqlite" - Parameterized queries: Always use
?placeholders - TypeScript: Explicit types for all functions
const result = await db.execute({
sql: `SELECT * FROM table WHERE id = ?`,
args: [id],
});
const row = firstRow<TypeName>(result);
const id = crypto.randomUUID();
const now = Date.now();
await db.execute({
sql: `INSERT INTO table (id, field, created_at) VALUES (?, ?, ?)`,
args: [id, value, now],
});
API endpoints are split between two patterns matching Django structure:
API Pattern (under /api/ prefix):
app.get("/api/recordings", authMiddleware, async (c) => {
const user = c.get("user");
const db = getDb();
const result = await db.execute({
sql: `SELECT * FROM recordings WHERE user_id = ?`,
args: [user.id],
});
return c.json({ recordings: parseRows(result) });
});
Action Pattern (root level):
app.post("/recordings/upload", authMiddleware, async (c) => {
const user = c.get("user");
const body = await c.req.json();
// Handle upload logic
return c.json({ success: true });
});
- API endpoints (
/api/*): Read operations, list views - Action endpoints (root level): Create, update, action operations
- Webhooks (
/webhooks/*): External webhook handlers (no auth)
Tables:
users- User accounts (id, email, api_token, etc.)recordings- Meeting recordings (id, user_id, recallai_bot_id, title, etc.)clips- Video clips (id, recording_id, title, video)calendar_events- Calendar events (id, user_id, raw, start_time, end_time)calendar_preferences- User automation rules (id, user_id, enabled, ruleset)calendar_series_preferences- Recurring meeting preferences
Field types:
- IDs: TEXT (UUIDs)
- Timestamps: INTEGER (milliseconds)
- Booleans: INTEGER (0/1)
- JSON: TEXT (stringify/parse)
Access via Deno.env.get("VAR_NAME"):
RECALLAI_API_KEY- RequiredAWS_ACCESS_KEY_ID- RequiredAWS_SECRET_ACCESS_KEY- RequiredAWS_STORAGE_BUCKET_NAME- RequiredJWT_SECRET- Required
- β Split code into multiple files
- β Use JSR imports (
jsr:@hono/hono) - β Use local file system
- β Add voice print/ML features
- β Hardcode secrets
- β Use SQL string concatenation
- β
Keep everything in
val.ts - β Use esm.sh for imports
- β Use Val Town SQLite
- β Use parameterized queries
- β Handle errors gracefully
- β Log important operations
- β Return proper HTTP status codes
- Copy
val.tsto Val Town - Set environment variables in Val Town UI
- Create first user via script Val
- Test endpoints
# Health check curl https://yourval.web.val.run/health # List recordings curl -H "Authorization: Token YOUR_TOKEN" \ https://yourval.web.val.run/api/recordings # Send bot curl -X POST https://yourval.web.val.run/recordings/send_bot \ -H "Authorization: Token YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"meeting_url": "https://zoom.us/j/123", "title": "Test"}'
- Val Town: https://val.town
- Val Town Docs: https://docs.val.town
- Hono.js: https://hono.dev
- Recall.ai: https://docs.recall.ai