Complete Example
Deploy to CreateOS with plain fetch + viem. No SDK or special dependencies needed.
Prerequisites
Install viem:
Bash1npm install viem
Generate a wallet if you don't have one:
TypeScript1import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";2const privateKey = generatePrivateKey();3const account = privateKeyToAccount(privateKey);4console.log("Address:", account.address);5// Save privateKey securely
Fund the wallet with ETH (gas) and USDC on Arbitrum or Base.
Full Deploy Script
TypeScript1import { privateKeyToAccount } from "viem/accounts";2import { createWalletClient, createPublicClient, http } from "viem";3import { arbitrum } from "viem/chains";4import { randomUUID } from "crypto";56const GATEWAY = "https://mpp-createos.nodeops.network";7const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");89// --- Auth helper ---10const auth = async () => {11 const nonce = randomUUID();12 const timestamp = String(Date.now());13 const signature = await account.signMessage({14 message: `${account.address}:${timestamp}:${nonce}`,15 });16 return {17 "X-Wallet-Address": account.address,18 "X-Signature": signature,19 "X-Timestamp": timestamp,20 "X-Nonce": nonce,21 };22};2324// --- ERC20 ABI ---25const ERC20_ABI = [{26 name: "transfer",27 type: "function",28 stateMutability: "nonpayable",29 inputs: [30 { name: "to", type: "address" },31 { name: "value", type: "uint256" },32 ],33 outputs: [{ name: "", type: "bool" }],34}] as const;3536// --- Deploy body ---37const body = {38 uniqueName: `app-${Date.now()}`,39 displayName: "My App",40 upload: {41 type: "files",42 files: [43 {44 path: "index.js",45 content: btoa(46 'require("http").createServer((q,s) => s.end("Hello from CreateOS!")).listen(3000)'47 ),48 },49 { path: "package.json", content: btoa('{"name":"app"}') },50 ],51 },52};5354// 1. Get quote55const quoteRes = await fetch(`${GATEWAY}/agent/deploy`, {56 method: "POST",57 headers: { "Content-Type": "application/json", ...(await auth()) },58 body: JSON.stringify(body),59});6061if (quoteRes.ok) {62 // Already had credits - deployed for free63 const result = await quoteRes.json();64 console.log("Deployed (credits):", result);65} else if (quoteRes.status === 402) {66 const quote = await quoteRes.json();67 console.log(`Payment required: $${quote.amount_usd} ${quote.token}`);6869 // 2. Pay70 const walletClient = createWalletClient({71 account,72 chain: arbitrum,73 transport: http(),74 });75 const publicClient = createPublicClient({76 chain: arbitrum,77 transport: http(),78 });7980 const txHash = await walletClient.writeContract({81 address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum82 abi: ERC20_ABI,83 functionName: "transfer",84 args: [quote.pay_to, BigInt(quote.amount_token)],85 });86 await publicClient.waitForTransactionReceipt({ hash: txHash });87 console.log("Payment confirmed:", txHash);8889 // 3. Deploy with payment proof90 const deployRes = await fetch(`${GATEWAY}/agent/deploy`, {91 method: "POST",92 headers: {93 "Content-Type": "application/json",94 "X-Payment-Tx": txHash,95 "X-Payment-Chain": quote.payment_chain,96 "X-Payment-Token": quote.token,97 ...(await auth()),98 },99 body: JSON.stringify(body),100 });101 const { projectId, deploymentId } = await deployRes.json();102103 // 4. Poll until ready104 let endpoint;105 while (!endpoint) {106 await new Promise((r) => setTimeout(r, 5000));107 const statusRes = await fetch(108 `${GATEWAY}/agent/deploy/${projectId}/${deploymentId}/status`,109 { headers: await auth() }110 );111 const status = await statusRes.json();112 if (status.status === "ready") endpoint = status.endpoint;113 if (status.status === "failed") throw new Error(status.reason);114 console.log("Status:", status.status);115 }116117 console.log(`Live: ${endpoint}`);118}
Zip Upload
Replace the upload field in the body:
TypeScript1import { readFileSync } from "fs";23const body = {4 uniqueName: `app-${Date.now()}`,5 displayName: "My App",6 upload: {7 type: "zip",8 data: readFileSync("code.zip").toString("base64"),9 filename: "code.zip",10 },11};
List Your Projects
TypeScript1const res = await fetch(`${GATEWAY}/agent/projects`, {2 headers: await auth(),3});4const { projects } = await res.json();5projects.forEach((p) => console.log(`${p.name}: ${p.url ?? "deploying..."}`));
Check Balance
TypeScript1const res = await fetch(2 `${GATEWAY}/agent/balance/${account.address}?chain=arbitrum`3);4const { balances } = await res.json();5balances.forEach((b) => console.log(`${b.symbol}: ${b.balance}`));