Skip to content

Instantly share code, notes, and snippets.

@jchris
Last active August 6, 2025 00:44
Show Gist options
  • Save jchris/d6b81cec96a40ea7de83158f3d200773 to your computer and use it in GitHub Desktop.
Save jchris/d6b81cec96a40ea7de83158f3d200773 to your computer and use it in GitHub Desktop.

App Create API Documentation

Overview

The App Create API allows you to create and update AI-generated applications that are hosted on the Cloudflare Workers platform with dynamic subdomain routing.

Endpoint

POST https://vibes-diy-api.com/api/apps

Authentication

Authentication is optional for this endpoint. You can provide a JWT token via the X-VIBES-Token header for enhanced functionality:

X-VIBES-Token: <your-jwt-token>

With Authentication:

  • User email is attached to the app
  • User context is available for the application

Without Authentication:

  • App is still created successfully
  • userId and email fields will be null
  • All other functionality works normally

Request Body

Content-Type: application/json

Field Type Required Description
chatId string Unique identifier for the chat session (only required field)
userId string User identifier
code string The application code to save (defaults to empty string)
raw string Raw code before processing
prompt string The prompt used to generate the app
title string Display title for the app (defaults to "App {slug}")
screenshot string | null Base64 encoded screenshot image
remixOf string | null Slug of the original app if this is a remix
shareToFirehose boolean Whether to post to Bluesky

Example Requests

Minimal Request:

{
  "chatId": "chat_abc123"
}

Complete Request:

{
  "chatId": "chat_abc123",
  "userId": "user_456",
  "code": "function App() { return <div>Hello World</div>; }",
  "raw": "// Raw code here",
  "prompt": "Create a hello world app",
  "title": "My Hello World App",
  "screenshot": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
  "remixOf": null,
  "shareToFirehose": false
}

cURL Example:

curl -X POST "https://vibes-diy-api.com/api/apps" \
  -H "Content-Type: application/json" \
  -H "X-VIBES-Token: your-jwt-token" \
  -d '{"chatId": "my-app-123", "title": "My Test App"}'

Response

Success Response (200 OK)

{
  "success": true,
  "app": {
    "name": "app-1672531200000",
    "slug": "mighty-wave-42",
    "code": "function App() { return <div>Hello World</div>; }",
    "raw": "// Raw code here",
    "prompt": "Create a hello world app",
    "title": "My Hello World App",
    "templatedCode": null,
    "chatId": "chat_abc123",
    "userId": "user_456",
    "email": "[email protected]",
    "hasScreenshot": false,
    "screenshotKey": null,
    "remixOf": null,
    "updateCount": 0,
    "shareToFirehose": false
  }
}

Behavior

Creating New Apps

When creating a new app (chatId doesn't exist):

  • Generates a unique slug using the format {adjective}-{noun}-{number}
  • Sets updateCount to 0
  • Stores the app in Cloudflare KV using both chatId and slug as keys
  • Processes and stores screenshots if provided

Updating Existing Apps

When updating an existing app (chatId exists):

  • Updates the code, raw, and templatedCode fields
  • Increments the updateCount
  • Updates other fields only if provided in the request
  • Maintains the original slug and creation metadata

Screenshot Processing

If a screenshot is provided:

  • Removes the data URL prefix if present
  • Decodes base64 to binary data
  • Stores in KV with key format: {slug}-screenshot

Queue Events

After successful creation/update, an event is sent to the publish queue:

  • Event type: app_created for new apps, app_updated for updates
  • Includes full app data and metadata
  • Used for downstream processing (Discord notifications, etc.)

App URLs

Once created, apps are accessible at:

  • https://{slug}.vibesdiy.app - Live application
  • Apps are served through a templated HTML wrapper with React 19 and Tailwind CSS

Error Handling

The API will return appropriate HTTP status codes:

  • 200 - Success
  • 400 - Bad Request (validation errors, malformed JSON)
  • 500 - Internal Server Error

Note: Authentication errors do not return 401. Invalid or missing JWT tokens are ignored, and the request continues without user context.

Validation Errors

If required fields are missing or invalid, you'll receive a 400 response:

{
  "errors": [
    {
      "code": "invalid_type",
      "expected": "string",
      "received": "undefined",
      "path": ["body", "chatId"],
      "message": "Required"
    }
  ],
  "success": false,
  "result": {}
}

CORS Support

The API includes full CORS support:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: GET,HEAD,PUT,POST,DELETE,PATCH
  • OPTIONS preflight requests return 204

Rate Limiting

Rate limiting is handled at the Cloudflare Workers level. No authentication tier restrictions apply.

Technical Notes

  • Uses chanfana for OpenAPI schema validation with Zod
  • Screenshot processing is optional and handled asynchronously
  • Queue failures do not prevent app creation/update success
  • Apps are double-indexed in KV storage (both chatId and slug as keys)
  • JWT authentication uses ES256 algorithm with base58btc-encoded JWK public keys
  • Invalid authentication tokens are silently ignored (no error returned)
  • Apps are immediately accessible after creation at https://{slug}.vibesdiy.app

Authentication Details

JWT Token Requirements (when provided):

  • Header: X-VIBES-Token
  • Algorithm: ES256
  • Issuer: FP_CLOUD
  • Audience: PUBLIC
  • Must not be expired

Public Key Verification:

  • Server uses CLOUD_SESSION_TOKEN_PUBLIC_KEY environment variable
  • Fallback to CLOUD_SESSION_TOKEN_PUBLIC_KEY_DEV for development
  • Keys are base58btc-encoded JWK format

App Rendering System Documentation

Overview

The app rendering system handles the serving of AI-generated applications through dynamic subdomain routing. It takes stored application code and wraps it in a templated HTML environment with React 19, Tailwind CSS, and other dependencies.

Architecture

Main File: src/renderApp.ts
Template: src/apptemplate.html
Code Transform: src/utils/codeTransform.ts

Subdomain Routing

Applications are served via subdomains following the pattern: {slug}.vibesdiy.app

Domain Handling

  • App Subdomains: {slug}.vibesdiy.app - Serves the application
  • Apex Domains: vibesdiy.app, vibecode.garden - Redirects to https://vibes.diy
  • www Subdomain: Redirects to https://vibes.diy

Key Routes

GET / - Main App Rendering

Function: Serves the complete HTML application with embedded React code

Process:

  1. Extract subdomain from request URL
  2. Handle apex domain redirects
  3. Lookup app data in Cloudflare KV using subdomain as key
  4. Generate/retrieve API key for the app (stored in httpOnly cookie)
  5. Transform application code imports
  6. Inject code into HTML template
  7. Add meta tags for social sharing
  8. Return templated HTML

Features:

  • API Key Management: Automatically creates API keys for apps using createKey() function
  • Cookie Storage: Stores API key in secure httpOnly cookie (vibes-diy-app) for 1 hour
  • Remix Support: Shows remix button if app is derived from another app
  • Social Meta Tags: Generates OpenGraph and Twitter Card meta tags
  • Import Transformation: Converts npm package imports to ESM.sh URLs
  • Dynamic Fireproof Version: Supports version override via v_fp URL parameter

GET /App.jsx - Raw Code Access

Function: Returns the raw application code as JavaScript

Process:

  1. Extract subdomain and lookup app data
  2. Return raw code (prefers app.raw over app.code)
  3. Set CORS headers for cross-origin access
  4. Content-Type: application/javascript

CORS Headers:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: GET, OPTIONS
  • Access-Control-Allow-Headers: Content-Type, x-vibes-token

GET /screenshot.png - App Screenshots

Function: Serves app screenshots with HTTP range support

Process:

  1. Lookup screenshot in KV using {subdomain}-screenshot key
  2. Handle HTTP Range requests for partial content (206 responses)
  3. Return PNG image with proper caching headers

Features:

  • Range Requests: Supports partial content delivery
  • HEAD Requests: Returns headers without body
  • Caching: 24-hour cache control
  • Error Handling: Returns 416 for invalid ranges

Static Asset Routes

GET /favicon.svg & GET /favicon.ico

  • Serves favicon files from Cloudflare KV
  • 30-day cache control
  • Returns 404 if not found in KV

GET /babel.min.js

  • Serves Babel standalone from KV (babel-standalone key)
  • 24-hour cache control
  • Required for JSX transformation in browsers

Code Transformation

Import Map System

Library Import Map (src/utils/codeTransform.ts):

{
  react: "https://esm.sh/[email protected]/es2022/react.mjs",
  "react-dom": "https://esm.sh/[email protected]/es2022/react-dom.mjs",
  "react-dom/client": "https://esm.sh/[email protected]/es2022/client.mjs",
  "use-fireproof": "https://esm.sh/[email protected]",
  "call-ai": "https://esm.sh/call-ai",
  "use-vibes": "https://esm.sh/use-vibes",
  eruda: "https://esm.sh/eruda",
  three: "https://esm.sh/three"
}

Transform Process

  1. Protected Imports: Libraries in the import map remain unchanged
  2. URL Imports: Existing URLs (containing :// or starting with http) are preserved
  3. Relative Imports: Paths starting with ./ or ../ are preserved
  4. NPM Packages: Other imports are transformed to https://esm.sh/{package}

Dynamic Features

  • Fireproof Version Override: URL parameter v_fp can specify custom fireproof version
  • Version Validation: Uses semver pattern matching with fallback to default version
  • Runtime Import Map: Generated dynamically and injected into template

HTML Template Integration

Template Placeholders

  • {{APP_CODE}} - Transformed application code
  • {{API_KEY}} - Generated API key for the app
  • {{APP_SLUG}} - Application slug (multiple replacements)
  • {{REMIX_BUTTON}} - Remix button HTML if applicable
  • {{IMPORT_MAP}} - JSON import map for ES modules

Meta Tag Generation

Social Sharing Support:

  • OpenGraph tags (og:title, og:description, og:image, og:url, og:type)
  • Twitter Card tags (twitter:card, twitter:title, twitter:description, twitter:image, twitter:url)
  • Dynamic image URLs for screenshots: https://{subdomain}.vibesdiy.app/screenshot.png

Dependencies Included

  • React 19: Latest React with new features
  • Tailwind CSS 4: Utility-first CSS framework (browser version)
  • Babel Standalone: For JSX transformation
  • use-vibes CSS: Custom component styles

Error Handling

  • 404 Responses: When app not found in KV or invalid subdomain
  • 301 Redirects: For apex domains and www subdomain
  • 405 Method Not Allowed: For unsupported HTTP methods on screenshot endpoint
  • 416 Range Not Satisfiable: For invalid HTTP range requests

Security Features

  • httpOnly Cookies: API keys stored securely
  • Secure Cookies: HTTPS-only cookie transmission
  • CORS Support: Configurable cross-origin access
  • Input Validation: URL parsing and subdomain extraction

Performance Optimizations

  • Caching Headers: Long-term caching for static assets
  • Range Requests: Efficient screenshot delivery
  • Cookie-based Auth: Reduces API key generation overhead
  • KV Storage: Fast app data retrieval

Configuration

Environment Variables

  • SERVER_OPENROUTER_PROV_KEY: Provisioning key for API key generation

KV Storage Keys

  • {subdomain}: App data storage
  • {subdomain}-screenshot: Screenshot binary data
  • favicon.svg: SVG favicon
  • favicon.ico: ICO favicon
  • babel-standalone: Babel JavaScript library

App Accessibility

Once created via the App Create API, apps are immediately accessible:

  • URL format: https://{slug}.vibesdiy.app
  • No propagation delay - apps work instantly after creation
  • Verified through curl testing with live examples

Deployment Notes

  • Template is imported at build time: import template from "./apptemplate.html"
  • Static assets should be uploaded to KV during deployment
  • CORS policies allow broad access for development flexibility
  • All static assets are served with appropriate caching headers
  • Screenshots support HTTP range requests for efficient delivery

Vibes Chat API Documentation

Overview

The Vibes Chat API provides OpenAI-compatible chat completions powered by OpenRouter, giving anonymous users access to multiple AI models for building applications. This endpoint acts as a proxy to OpenRouter's API with built-in rate limiting and usage tracking.

Endpoints

The same functionality is available on two endpoints:

  • Primary: POST https://vibes-diy-api.com/api/v1/chat/completions
  • Explicit: POST https://vibes-diy-api.com/api/v1/openrouter/chat/completions

Authentication Methods

Method 1: Proxy-Managed (Recommended for Anonymous Users)

Use the special bearer token to let Vibes DIY handle API key management:

curl -X POST "https://vibes-diy-api.com/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-vibes-proxy-managed" \
  -d '{
    "model": "anthropic/claude-3.5-sonnet",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

Method 2: Bring Your Own Key (BYOK)

Provide your own OpenRouter API key:

curl -X POST "https://vibes-diy-api.com/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_OPENROUTER_KEY" \
  -d '{
    "model": "anthropic/claude-3.5-sonnet", 
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

Request Format

Required Fields

Field Type Description
model string ID of the model to use (see Available Models)
messages array Conversation messages with role and content

Optional Parameters

Parameter Type Default Description
temperature number 1 Sampling temperature (0-2)
top_p number 1 Nucleus sampling parameter
n number 1 Number of completion choices
stream boolean false Enable streaming responses
max_tokens number - Maximum tokens to generate
presence_penalty number 0 Presence penalty (-2 to 2)
frequency_penalty number 0 Frequency penalty (-2 to 2)
logit_bias object - Token likelihood modifiers
response_format object - Response format (json/text)
seed number - Deterministic sampling seed

Message Format

{
  "role": "user|assistant|system",
  "content": "Message content",
  "name": "optional_author_name"
}

Available Models

Popular models available through OpenRouter:

Claude Models (Verified ✅)

  • anthropic/claude-3.5-sonnet - Best reasoning and coding
  • anthropic/claude-3-haiku - Fastest and most affordable
  • anthropic/claude-3-opus - Most capable for complex tasks

GPT Models (Verified ✅)

  • openai/gpt-4o - Latest GPT-4 model
  • openai/gpt-4o-mini - Affordable GPT-4 variant
  • openai/gpt-3.5-turbo - Fast and economical

Open Source Models (Verified ✅)

  • meta-llama/llama-3.1-8b-instruct - Meta's Llama 8B
  • meta-llama/llama-3.1-70b-instruct - Meta's Llama 70B
  • mistralai/mixtral-8x7b-instruct - Mixture of experts
  • google/gemma-2-9b-it - Google's Gemma

For complete model list, see OpenRouter Models

Response Format

Standard Response

{
  "id": "gen-1754440868-PvAlFLFaGODEnCQ7jqM2",
  "provider": "Anthropic",
  "model": "anthropic/claude-3-haiku",
  "object": "chat.completion",
  "created": 1754440868,
  "choices": [{
    "logprobs": null,
    "finish_reason": "stop",
    "native_finish_reason": "stop",
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "Hello! How can I help you today?",
      "refusal": null,
      "reasoning": null
    }
  }],
  "usage": {
    "prompt_tokens": 15,
    "completion_tokens": 9,
    "total_tokens": 24
  }
}

OpenRouter-Specific Fields:

  • provider: The AI provider (e.g., "Anthropic", "OpenAI")
  • native_finish_reason: Provider-specific finish reason
  • logprobs: Log probabilities (usually null)
  • refusal: Content refusal information (usually null)
  • reasoning: Reasoning information (usually null)

Streaming Response

When stream: true, responses arrive as Server-Sent Events:

: OPENROUTER PROCESSING

data: {"id":"gen-1754440902-f4Qht5GQGbl1FhurEd1c","provider":"Anthropic","model":"anthropic/claude-3-haiku","object":"chat.completion.chunk","created":1754440902,"choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}

data: {"id":"gen-1754440902-f4Qht5GQGbl1FhurEd1c","provider":"Anthropic","model":"anthropic/claude-3-haiku","object":"chat.completion.chunk","created":1754440902,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}

data: {"id":"gen-1754440902-f4Qht5GQGbl1FhurEd1c","provider":"Anthropic","model":"anthropic/claude-3-haiku","object":"chat.completion.chunk","created":1754440902,"choices":[{"index":0,"delta":{},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":15,"completion_tokens":12,"total_tokens":27}}

data: [DONE]

Streaming Notes:

  • OpenRouter starts with : OPENROUTER PROCESSING comment
  • Final chunk includes usage statistics
  • All chunks include OpenRouter-specific fields

Example Requests

Basic Chat

const response = await fetch('https://vibes-diy-api.com/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk-vibes-proxy-managed'
  },
  body: JSON.stringify({
    model: 'anthropic/claude-3.5-sonnet',
    messages: [
      {role: 'system', content: 'You are a helpful coding assistant.'},
      {role: 'user', content: 'Explain React hooks in simple terms.'}
    ],
    temperature: 0.7,
    max_tokens: 500
  })
});

const data = await response.json();
console.log(data.choices[0].message.content);

Streaming Chat

const response = await fetch('https://vibes-diy-api.com/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk-vibes-proxy-managed'
  },
  body: JSON.stringify({
    model: 'anthropic/claude-3.5-sonnet',
    messages: [{role: 'user', content: 'Write a short poem'}],
    stream: true
  })
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  const chunk = decoder.decode(value);
  const lines = chunk.split('\n');
  
  for (const line of lines) {
    if (line.startsWith('data: ') && line !== 'data: [DONE]') {
      const data = JSON.parse(line.slice(6));
      if (data.choices[0].delta.content) {
        process.stdout.write(data.choices[0].delta.content);
      }
    }
  }
}

JSON Mode

{
  "model": "anthropic/claude-3.5-sonnet",
  "messages": [
    {
      "role": "user", 
      "content": "Generate a JSON object with user data for John, age 30"
    }
  ],
  "response_format": {"type": "json"}
}

Error Handling

Common Error Responses

401 - Missing Authorization

{"error": "Authorization header required"}

500 - Server Configuration

{"error": "OpenRouter API key not configured"}

OpenRouter API Errors

{
  "error": "Failed to get chat completion",
  "details": {
    "error": {
      "message": "nonexistent/model is not a valid model ID",
      "code": 400
    },
    "user_id": "org_2yMWxAYjNAllI48mRpQXJdUPxqc"
  }
}

Authentication Context

With X-VIBES-Token Header

Include user authentication for enhanced features:

curl -X POST "https://vibes-diy-api.com/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-vibes-proxy-managed" \
  -H "X-VIBES-Token: YOUR_JWT_TOKEN" \
  -d '{"model": "anthropic/claude-3.5-sonnet", "messages": [{"role": "user", "content": "Hello!"}]}'

Anonymous Usage

The API works without X-VIBES-Token - just provide Authorization header:

curl -X POST "https://vibes-diy-api.com/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-vibes-proxy-managed" \
  -d '{"model": "anthropic/claude-3.5-sonnet", "messages": [{"role": "user", "content": "Hello!"}]}'

Rate Limiting & Usage

  • Proxy-Managed: Rate limited by server configuration
  • BYOK: Limited by your OpenRouter account
  • IP Tracking: All requests logged with client IP
  • Usage Monitoring: Token usage tracked for analytics

CORS Support

Full CORS support for browser-based applications:

  • All origins allowed (Access-Control-Allow-Origin: *)
  • Standard HTTP methods supported
  • Streaming responses include appropriate headers

Integration with Vibe Apps

When called from a Vibe DIY app:

  • Referer header automatically set to app URL
  • X-Title set to "Vibes DIY" for OpenRouter analytics
  • User context preserved if authenticated

Best Practices

Model Selection

  • Development: Use anthropic/claude-3-haiku for speed/cost
  • Production: Use anthropic/claude-3.5-sonnet for quality
  • Complex Tasks: Use anthropic/claude-3-opus for reasoning

Error Handling

try {
  const response = await fetch(endpoint, options);
  if (!response.ok) {
    const error = await response.json();
    console.error('API Error:', error);
    return;
  }
  const data = await response.json();
} catch (error) {
  console.error('Network Error:', error);
}

Streaming Best Practices

  • Always handle connection errors
  • Implement proper cleanup for aborted streams
  • Parse SSE format correctly

Security Notes

  • API keys are never exposed in responses
  • All requests logged with anonymized IP addresses
  • User-provided keys are not stored or cached
  • HTTPS required for all requests

API Testing & Verification

All endpoints and features have been tested with curl:

Basic Completions: Both endpoints work perfectly
Streaming: SSE format works with proper [DONE] termination
Error Handling: 401 for missing auth, detailed API errors
Multiple Models: Claude, GPT, and Llama models all verified
Proxy Authentication: sk-vibes-proxy-managed works flawlessly

Test Examples

# Basic completion
curl -X POST "https://vibes-diy-api.com/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-vibes-proxy-managed" \
  -d '{"model": "anthropic/claude-3-haiku", "messages": [{"role": "user", "content": "Hello"}]}'

# Streaming
curl -X POST "https://vibes-diy-api.com/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-vibes-proxy-managed" \
  -d '{"model": "anthropic/claude-3-haiku", "messages": [{"role": "user", "content": "Count to 3"}], "stream": true}' -N

Pricing

  • Proxy-Managed: Included in Vibes DIY platform usage
  • BYOK: Billed directly by OpenRouter at their rates
  • No Markup: BYOK users pay OpenRouter prices directly
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment