Skip to content
LogoLogo

Complete Example

Deploy to CreateOS with plain fetch + viem. No SDK or special dependencies needed.

Prerequisites

Install viem:

npm install viem

Generate a wallet if you don't have one:

import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);
console.log("Address:", account.address);
// Save privateKey securely

Fund the wallet with ETH (gas) and USDC on Arbitrum or Base.

Full Deploy Script

import { privateKeyToAccount } from "viem/accounts";
import { createWalletClient, createPublicClient, http } from "viem";
import { arbitrum } from "viem/chains";
import { randomUUID } from "crypto";
 
const GATEWAY = "https://mpp-createos.nodeops.network";
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
 
// --- Auth helper ---
const auth = async () => {
  const nonce = randomUUID();
  const timestamp = String(Date.now());
  const signature = await account.signMessage({
    message: `${account.address}:${timestamp}:${nonce}`,
  });
  return {
    "X-Wallet-Address": account.address,
    "X-Signature": signature,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
  };
};
 
// --- ERC20 ABI ---
const ERC20_ABI = [
  {
    name: "transfer",
    type: "function",
    stateMutability: "nonpayable",
    inputs: [
      { name: "to", type: "address" },
      { name: "value", type: "uint256" },
    ],
    outputs: [{ name: "", type: "bool" }],
  },
] as const;
 
// --- Deploy body ---
const body = {
  uniqueName: `app-${Date.now()}`,
  displayName: "My App",
  upload: {
    type: "files",
    files: [
      {
        path: "index.js",
        content: btoa(
          'require("http").createServer((q,s) => s.end("Hello from CreateOS!")).listen(3000)',
        ),
      },
      { path: "package.json", content: btoa('{"name":"app"}') },
    ],
  },
};
 
// 1. Get quote
const quoteRes = await fetch(`${GATEWAY}/agent/deploy`, {
  method: "POST",
  headers: { "Content-Type": "application/json", ...(await auth()) },
  body: JSON.stringify(body),
});
 
if (quoteRes.ok) {
  // Already had credits - deployed for free
  const result = await quoteRes.json();
  console.log("Deployed (credits):", result);
} else if (quoteRes.status === 402) {
  const quote = await quoteRes.json();
  console.log(`Payment required: $${quote.amount_usd} ${quote.token}`);
 
  // 2. Pay
  const walletClient = createWalletClient({
    account,
    chain: arbitrum,
    transport: http(),
  });
  const publicClient = createPublicClient({
    chain: arbitrum,
    transport: http(),
  });
 
  const txHash = await walletClient.writeContract({
    address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
    abi: ERC20_ABI,
    functionName: "transfer",
    args: [quote.pay_to, BigInt(quote.amount_token)],
  });
  await publicClient.waitForTransactionReceipt({ hash: txHash });
  console.log("Payment confirmed:", txHash);
 
  // 3. Deploy with payment proof
  const deployRes = await fetch(`${GATEWAY}/agent/deploy`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Payment-Tx": txHash,
      "X-Payment-Chain": quote.payment_chain,
      "X-Payment-Token": quote.token,
      ...(await auth()),
    },
    body: JSON.stringify(body),
  });
  const { projectId, deploymentId } = await deployRes.json();
 
  // 4. Poll until ready
  let endpoint;
  while (!endpoint) {
    await new Promise((r) => setTimeout(r, 5000));
    const statusRes = await fetch(
      `${GATEWAY}/agent/deploy/${projectId}/${deploymentId}/status`,
      { headers: await auth() },
    );
    const status = await statusRes.json();
    if (status.status === "ready") endpoint = status.endpoint;
    if (status.status === "failed") throw new Error(status.reason);
    console.log("Status:", status.status);
  }
 
  console.log(`Live: ${endpoint}`);
}

Zip Upload

Replace the upload field in the body:

import { readFileSync } from "fs";
 
const body = {
  uniqueName: `app-${Date.now()}`,
  displayName: "My App",
  upload: {
    type: "zip",
    data: readFileSync("code.zip").toString("base64"),
    filename: "code.zip",
  },
};

List Your Projects

const res = await fetch(`${GATEWAY}/agent/projects`, {
  headers: await auth(),
});
const { projects } = await res.json();
projects.forEach((p) => console.log(`${p.name}: ${p.url ?? "deploying..."}`));

Check Balance

const res = await fetch(
  `${GATEWAY}/agent/balance/${account.address}?chain=arbitrum`,
);
const { balances } = await res.json();
balances.forEach((b) => console.log(`${b.symbol}: ${b.balance}`));