Complete Example
Deploy to CreateOS with plain fetch + viem. No SDK or special dependencies needed.
Prerequisites
Install viem:
npm install viemGenerate 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 securelyFund 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}`));