Deploy to Crayy

Get your app live in under a minute. Works with Node.js, Flask, FastAPI, Streamlit, Gradio, and static HTML.

1. Quick Start

Fastest way: Open your project in Claude Code and say: "deploy this to crayy.com"

Claude Code (ADA) will read these docs, walk you through naming your app, and handle the deploy automatically. Or follow the manual steps below.

2. App Requirements

3. Deploy Steps

These steps are written for both humans and AI assistants (like Claude Code) to follow programmatically.

Step 1: Check for existing deploy config

Look for .crayy/config.json in the project root. If it exists, this is an update — skip to the Update section below.

Step 2: Choose an app name

Ask the user for their desired app name. Rules:

Step 3: Verify name availability

GET https://mvp.crayy.com/api/check-name/{name}

Response:

{ "name": "my-app", "available": true }

If available is false, ask the user for a different name.

Step 4: Get user's email

Ask the user for their email address. This is used for deploy notifications and account association.

Step 5: Ensure /health endpoint exists

Check the app's source code for a /health endpoint. If missing, add one based on the framework. The server-side review agent will also attempt to auto-fix this, but adding it client-side gives faster deploys.

Step 6: Create the tarball

tar -czf app.tar.gz --exclude=node_modules --exclude=__pycache__ --exclude=.git --exclude=.env --exclude=.crayy -C <appDir> .

Step 7: Deploy

POST https://mvp.crayy.com/api/deploy
Content-Type: multipart/form-data

Fields:
  tarball: (file) app.tar.gz
  name: my-app
  displayName: My Cool App
  description: One-line description of what the app does
  email: user@example.com

Step 8: Handle the response

Check the status field in the JSON response:

"deployed" — Success! Save the deploy token and report the URL:

{
  "status": "deployed",
  "app": { "name": "my-app", "url": "https://mvp.crayy.com/my-app", "displayName": "My Cool App" },
  "deployToken": "abc123...",
  "oaChanges": [],
  "message": "App deployed successfully!"
}

Save deployToken, name, and url to .crayy/config.json in the project root for future updates:

{
  "appName": "my-app",
  "deployToken": "abc123...",
  "url": "https://mvp.crayy.com/my-app"
}

"rejected" — The review agent rejected the app:

{
  "status": "rejected",
  "reason": "App contains potentially malicious code: shell injection via user input.",
  "securityNotes": "...",
  "contentNotes": "..."
}

Report the rejection reason to the user. They need to fix the issues and try again.

"failed" — Build or health check failed:

{
  "status": "failed",
  "error": "Health check failed after all retries",
  "logs": "container stderr output..."
}

Report the error and container logs to the user so they can debug.

4. Update / Redeploy

When .crayy/config.json exists, the app has been deployed before. To update:

Step 1: Read the config

// .crayy/config.json
{
  "appName": "my-app",
  "deployToken": "abc123...",
  "url": "https://mvp.crayy.com/my-app"
}

Step 2: Create the tarball

tar -czf app.tar.gz --exclude=node_modules --exclude=__pycache__ --exclude=.git --exclude=.env --exclude=.crayy -C <appDir> .

Step 3: Deploy with token

POST https://mvp.crayy.com/api/deploy
Content-Type: multipart/form-data

Fields:
  tarball: (file) app.tar.gz
  name: my-app
  displayName: My Cool App
  description: Updated description
  email: user@example.com
  deployToken: abc123...

Step 4: Handle the response

Same response shapes as new deploy, except status will be "updated" on success:

{
  "status": "updated",
  "app": { "name": "my-app", "url": "https://mvp.crayy.com/my-app", "displayName": "My Cool App" },
  "oaChanges": [],
  "message": "App updated successfully!"
}

5. Adding /health — Framework Examples

Express (Node.js)

app.get('/health', (req, res) => res.status(200).json({ status: 'ok' }));

Flask

@app.route('/health')
def health():
    return {'status': 'ok'}, 200

FastAPI

@app.get('/health')
def health():
    return {'status': 'ok'}

Streamlit

Built-in health at /_stcore/health. No action needed.

Gradio

Built-in health at /. No action needed.

Static HTML

Create a health file (no extension) or health/index.html with any content. Or just ensure your index.html exists — the platform nginx config handles it.

6. API Reference

Check Name Availability

GET /api/check-name/:name

Response:
{ "name": "my-app", "available": true }

Deploy / Update App

POST /api/deploy
Content-Type: multipart/form-data

Fields:
  tarball: file (required) — .tar.gz of your app
  name: string (required) — app name, lowercase alphanumeric + hyphens
  displayName: string (optional) — human-readable name
  description: string (optional) — one-line description
  email: string (optional) — creator email
  deployToken: string (optional) — include for updates

Response (success - new deploy):
{
  "status": "deployed",
  "app": { "name": "...", "url": "https://mvp.crayy.com/...", "displayName": "..." },
  "deployToken": "...",
  "oaChanges": [...],
  "message": "App deployed successfully!"
}

Response (success - update):
{
  "status": "updated",
  "app": { "name": "...", "url": "https://mvp.crayy.com/...", "displayName": "..." },
  "oaChanges": [...],
  "message": "App updated successfully!"
}

Response (rejected by review):
{
  "status": "rejected",
  "reason": "...",
  "securityNotes": "...",
  "contentNotes": "..."
}

Response (build/health failure):
{
  "status": "failed",
  "error": "...",
  "logs": "..."
}

Discover Apps

GET /api/discover

Response:
{
  "platform": "crayy",
  "version": "0.1.0",
  "apps": [
    {
      "name": "my-app",
      "displayName": "My App",
      "description": "...",
      "type": "node",
      "url": "/my-app/",
      "status": "live",
      "profile": {
        "category": "...",
        "summary": "...",
        "capabilities": ["..."],
        "tags": ["..."]
      }
    }
  ]
}