Deploy Flow
The MPP deploy flow uses HTTP 402 responses to communicate payment requirements. The agent calls the deploy endpoint, handles the 402, pays on-chain, and retries.
Step 1: Request Deploy (Get Quote)
Call POST /agent/deploy without a payment header. The gateway checks your credit balance and active projects:
POST /agent/deploy
Content-Type: application/json
X-Wallet-Address: 0x...
X-Signature: 0x...
X-Timestamp: 1711500000000
X-Nonce: unique-uuid
{
"uniqueName": "my-app",
"displayName": "My App",
"upload": {
"type": "files",
"files": [
{ "path": "index.js", "content": "base64..." },
{ "path": "package.json", "content": "base64..." }
]
}
}
Response Cases
Has credits, no active projects - deploys immediately (200):
{ "projectId": "uuid", "deploymentId": "uuid", "status": "deploying" }Has credits, active projects exist - returns 402 with a warning:
{
"error": "Payment required",
"warning": "You have 2 active project(s) sharing credits...",
"amount_usd": 0.5,
"current_credit_balance_usd": 1.20,
"active_projects": 2,
"pay_to": "0x7EA5...",
"payment_chain": "arbitrum",
"token": "usdc",
"decimals": 6,
"supported_chains": [...]
}To deploy using existing credits (reduces runtime of other projects), retry with header X-Use-Existing-Credits: true.
No credits - returns 402 with payment details:
{
"error": "Payment required",
"amount_usd": 0.5,
"amount_token": "500000",
"current_credit_balance_usd": 0,
"active_projects": 0,
"pay_to": "0x7EA5...",
"payment_chain": "arbitrum",
"token": "usdc",
"decimals": 6,
"supported_chains": [
{ "chain": "arbitrum", "chain_id": 42161, "tokens": ["usdc", "usdt"] },
{ "chain": "base", "chain_id": 8453, "tokens": ["usdc", "usdt"] }
]
}Step 2: Check Balance
Before paying, verify the wallet has enough tokens:
GET /agent/balance/0xYourWallet?chain=arbitrum
{
"address": "0x...",
"chain": "arbitrum",
"balances": [
{ "token": "usdc", "symbol": "USDC", "balance": "18.000000", "decimals": 6 }
]
}If the balance is insufficient, prompt the user to fund the wallet on one of the supported chains.
Step 3: Pay On-Chain
Send an ERC20 transfer to the pay_to address from the 402 response:
import { createWalletClient, createPublicClient, http } from "viem";
import { arbitrum } from "viem/chains";
const ERC20_ABI = [
{
name: "transfer",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
],
outputs: [{ name: "", type: "bool" }],
},
] as const;
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 });Step 4: Deploy with Payment Proof
Retry the deploy request with the transaction hash:
POST /agent/deploy
Content-Type: application/json
X-Payment-Tx: 0xTransactionHash
X-Payment-Chain: arbitrum
X-Payment-Token: usdc
X-Wallet-Address: 0x...
X-Signature: 0x...
X-Timestamp: 1711500000000
X-Nonce: new-unique-uuid
Same body as Step 1. Response:
{ "projectId": "uuid", "deploymentId": "uuid", "status": "deploying" }Step 5: Poll Status
Poll the status endpoint every 5 seconds until the deployment is ready:
GET /agent/deploy/{projectId}/{deploymentId}/status
Possible responses:
| Status | Meaning |
|---|---|
deploying | Build/deploy in progress |
ready | Live at endpoint URL |
failed | Deployment failed with reason |
{ "status": "ready", "endpoint": "https://my-app.nodeops.network" }Upload Types
Files - array of base64-encoded files:
{
"type": "files",
"files": [
{ "path": "index.js", "content": "base64..." },
{ "path": "package.json", "content": "base64..." }
]
}Zip - base64-encoded zip archive:
{
"type": "zip",
"data": "base64-zip-content",
"filename": "code.zip"
}Exclude from uploads: node_modules/, dist/, build/, .next/, .env, .git/, __pycache__/, venv/