Step 15: Deploy to run402

Phase: deploy

Context

You have verified app_files and project_id. Time to put the app online.

What to do

Deploy the static site

POST https://api.run402.com/deployments/v1
Content-Type: application/json

{
  "name": "app-name",
  "project": "{project_id}",
  "files": [
    {
      "file": "index.html",
      "data": "<!DOCTYPE html>...",
      "encoding": "utf-8"
    },
    {
      "file": "style.css",
      "data": "body { ... }",
      "encoding": "utf-8"
    },
    {
      "file": "app.js",
      "data": "const CONFIG = ...",
      "encoding": "utf-8"
    }
  ]
}

This endpoint is x402-gated ($0.05). No API key or auth header is needed — the x402 payment header IS the authorization. Same flow as project creation: first request returns 402, sign payment, retry with x-402-payment header.

Response (201):

{
  "id": "dpl-1709337600000-a1b2c3",
  "name": "app-name",
  "url": "https://dpl-1709337600000-a1b2c3.sites.run402.com",
  "status": "READY",
  "files_count": 3,
  "total_size": 15234
}

Auth model reference

Different run402 endpoints use different authentication methods. Here is the complete reference:

EndpointAuth Method
POST /projects/v1x-402-payment header (x402 signed payment)
POST /deployments/v1x-402-payment header (x402 signed payment)
POST /projects/v1/admin/{id}/sqlAuthorization: Bearer {service_key}
POST /subdomains/v1Authorization: Bearer {service_key}
GET /rest/v1/...apikey header (anon_key)

Note: POST /subdomains/v1 is idempotent — it upserts. If the subdomain already belongs to your project, it updates the deployment_id instead of returning a 409. This means you can safely call it on every deploy without checking whether the subdomain already exists.

Important notes

  • Max 50 MB per deployment. If files exceed this, reduce image sizes or split assets.
  • Immutable deployments — each deploy creates a new URL. Previous deployments stay live.
  • SPA support — paths without file extensions automatically serve index.html.
  • For binary files (images), use "encoding": "base64" and base64-encode the data.

Verify deployment

Check deployment status (free, no auth):

GET https://api.run402.com/deployments/v1/{deployment_id}

Wait for "status": "READY" before sharing the URL.

Claim a subdomain

By default, claim a subdomain so the app has a memorable URL like https://myapp.run402.com instead of the raw deployment URL.

Derive the subdomain automatically: take the app name, lowercase it, replace spaces and underscores with hyphens, strip non-alphanumeric characters (except hyphens), collapse consecutive hyphens, and truncate to 63 characters. Only skip the subdomain if the user explicitly declines.

POST https://api.run402.com/subdomains/v1
Content-Type: application/json
Authorization: Bearer {service_key}

{
  "name": "myapp",
  "deployment_id": "{deployment_id}"
}

Response (201):

{
  "name": "myapp",
  "deployment_id": "dpl-1709337600000-a1b2c3",
  "url": "https://myapp.run402.com",
  "deployment_url": "https://dpl-1709337600000-a1b2c3.sites.run402.com",
  "project_id": "prj-abc123",
  "created_at": "2026-03-05T12:00:00Z",
  "updated_at": "2026-03-05T12:00:00Z"
}

Subdomain rules

  • 3-63 characters, lowercase letters, numbers, and hyphens only
  • Must start and end with a letter or number (no leading/trailing hyphens)
  • No consecutive hyphens (--)
  • Reserved words are blocked: api, www, admin, app, dashboard, docs, help, support, cdn, static, dev, staging, test, demo, run402, and others
  • Each subdomain is owned by the project that created it — other projects cannot claim it
  • Free — no x402 payment required, but needs service_key auth

If subdomain claiming fails

The subdomain request may fail for several reasons:

  • 409 Conflict — the name is already taken. Suggest a different name (e.g., add a number or prefix).
  • 400 Bad Request — the name violates the rules above (too short, reserved word, invalid characters).
  • 429 Rate Limit — too many requests. Wait a moment and retry once.
  • 5xx Server Errorrun402 issue. Retry once; if it persists, skip the subdomain.

Fallback: If subdomain claiming fails after one retry, fall back to the raw deployment URL (https://dpl-{id}.sites.run402.com). Tell the user their app is live at the deployment URL and that you can try claiming a memorable subdomain later.

Do not retry in a loop or stall — the deployment itself already succeeded. The subdomain is optional polish.

If the user explicitly declines a subdomain, skip it — the raw deployment URL works fine.

Smoke-test gate

After deployment (and optional subdomain), you MUST verify the app is actually live before proceeding.

  1. Fetch the live URL (prefer subdomain_url if claimed, otherwise deployment_url).
  2. Confirm the response is HTTP 200 and the HTML page loads (check for <!DOCTYPE html> or your app's <title>).
  3. If the fetch fails or returns non-200, wait 5 seconds and retry once.
  4. If it still fails, tell the user: "The deploy succeeded but the site isn't responding yet. Let's give it a moment." Retry up to 3 times total.
  5. Do NOT proceed to Step 16 until the smoke test passes.

Expected output

  • deployment_id — The deployment identifier
  • deployment_url — The live URL (e.g., https://dpl-xxx.sites.run402.com)
  • subdomain — The claimed subdomain name (if any)
  • subdomain_url — The memorable URL (e.g., https://myapp.run402.com)

Memory directive