Skip to Content
DocsUse CasesPrompt Contracts

Prompt Contracts

A prompt contract is a first-class primitive that wraps every inference request with a token budget envelope and pre-validation. While prompt contracts use TypeScript interfaces rather than .lode schema files, they follow the same trust-and-verify model.

Contract Structure

interface PromptContract { id: string; // Unique contract ID (UUID) userId: string; // Caller identity nodeKeyId?: string; // Operator key (legacy auth) stackId?: string; // Stack identifier ownerId?: string; // Account owner tokenLimit: number; // Hard ceiling (default: 1M) estimatedInboundCost: number; // Measured from request maxPossibleCost: number; // inbound + tokenLimit callerBalance: number; // Balance at creation time affordable: boolean; // Can support worst case? model: string; // Resolved model ID contentType: string; // "text" | "image" | "code" | "think" taskType: string; // "ai-prompt" | "coder-session" | etc. createdAt: number; // Timestamp }

How It Works

1. Build the Contract

import { buildPromptContract } from "@stacknet/prompts"; const contract = await buildPromptContract({ auth: { userId: "u_abc", ownerId: "owner_xyz" }, body: requestBody, inboundUsage: { textTokens: 4200, imageMegapixels: 0, videoMegabytes: 0, audioMegabytes: 0, }, contentType: "code", taskType: "coder-session", model: "qwen3-coder:30b", accountBalanceManager, });

2. Enforce the Contract

import { enforcePromptContract } from "@stacknet/prompts"; const rejection = enforcePromptContract(contract); if (rejection) return rejection; // HTTP 402 response // Proceed with inference...

3. Rejection Response

If the caller cannot afford the request, enforcePromptContract returns a 402 response:

{ "error": "Insufficient token balance", "code": "INSUFFICIENT_BALANCE", "required": 1004200, "available": 500000, "tokenLimit": 1000000, "estimatedInboundCost": 4200, "suggestion": "Lower token_limit or purchase more tokens" }

Balance Priority

The contract enforcer checks balances in priority order:

  1. Account balance — For users authenticated with sk_ keys or JWT. Checked via accountBalanceManager.getBalance(ownerId).
  2. Node key balance — For operators using nk_ keys directly. Checked via nodeKeyManager.getKeyBalance(nodeKeyId).
  3. Internal auth — Service-to-service calls. Always affordable (balance = Infinity).

Constants

ConstantValueDescription
DEFAULT_TOKEN_LIMIT1,000,000Default if token_limit is not specified
MAX_TOKEN_LIMIT10,000,000Hard ceiling, cannot be exceeded

Token Limit Extraction

The token limit is extracted from the request body and clamped:

const rawLimit = body.token_limit ?? body.max_tokens ?? DEFAULT_TOKEN_LIMIT; const tokenLimit = Math.min(Math.max(1, rawLimit), MAX_TOKEN_LIMIT);

Content Types

TypeDescription
textText generation
imageImage generation
videoVideo generation
musicMusic generation
codeCode generation
thinkReasoning/thinking

Task Types

TypeDescription
ai-promptStandard inference request
mcp-toolMCP tool execution
coder-sessionInteractive coding session
music-pipelineMulti-step music generation
Last updated on