OutLayer Documentation
Developer Earnings
Earn USD stablecoins when users call your OutLayer projects via HTTPS API. Users pay you directly through the X-Attached-Deposit header.
How Earnings Work#
When users call your project via HTTPS API, they can attach a payment to you using the X-Attached-Deposit header. This payment goes directly to your earnings balance.
Revenue model: You set your own pricing. Communicate required payment amounts in your documentation. Your WASM code can check the payment and adjust functionality accordingly (free tier, basic, premium, etc.).
Payment Flow#
- User makes API call with
X-Attached-Deposit: 100000($0.10) - Payment is charged immediately from user's Payment Key balance
- Your WASM receives
USD_PAYMENT="100000"environment variable - On completion, payment is added to your
project_owner_earnings - You can withdraw accumulated earnings to your NEAR wallet
Important: X-Attached-Deposit is charged even if execution fails. This prevents abuse where users trigger errors to avoid payment.
Checking Payment in WASM#
Your WASM code accesses the attached payment via the USD_PAYMENT environment variable. The value is in micro-units (1,000,000 = $1.00).
// Read USD payment (micro-units: 1000000 = $1.00)
let usd_payment: u64 = std::env::var("USD_PAYMENT")
.unwrap_or_else(|_| "0".to_string())
.parse()
.unwrap_or(0);
// Define pricing tiers
const FREE_TIER: u64 = 0;
const BASIC_TIER: u64 = 10_000; // $0.01
const PREMIUM_TIER: u64 = 100_000; // $0.10
const ENTERPRISE_TIER: u64 = 1_000_000; // $1.00
// Implement tiered functionality
if usd_payment >= ENTERPRISE_TIER {
// Full enterprise features
enterprise_analysis(&input)?;
} else if usd_payment >= PREMIUM_TIER {
// Premium features
premium_processing(&input)?;
} else if usd_payment >= BASIC_TIER {
// Basic paid features
basic_processing(&input)?;
} else {
// Free tier - limited functionality
free_preview(&input)?;
}Payment Validation#
You can reject requests that don't meet your minimum payment requirement:
const MIN_PAYMENT_USD: u64 = 50_000; // $0.05 minimum
let usd_payment: u64 = std::env::var("USD_PAYMENT")
.unwrap_or_else(|_| "0".to_string())
.parse()
.unwrap_or(0);
if usd_payment < MIN_PAYMENT_USD {
eprintln!("{{"error": "Minimum payment is $0.05. Please set X-Attached-Deposit: 50000"}}");
std::process::exit(1);
}
// Continue with paid functionality...NEAR vs HTTPS Payments#
For NEAR transactions, check NEAR_PAYMENT_YOCTO instead:
let execution_type = std::env::var("OUTLAYER_EXECUTION_TYPE")
.unwrap_or_else(|_| "NEAR".to_string());
let payment_sufficient = match execution_type.as_str() {
"HTTPS" => {
let usd = std::env::var("USD_PAYMENT")
.unwrap_or_else(|_| "0".to_string())
.parse::<u64>()
.unwrap_or(0);
usd >= 50_000 // $0.05 in USD
}
"NEAR" => {
let near = std::env::var("NEAR_PAYMENT_YOCTO")
.unwrap_or_else(|_| "0".to_string())
.parse::<u128>()
.unwrap_or(0);
near >= 50_000_000_000_000_000_000_000 // 0.05 NEAR in yocto
}
_ => false,
};
if !payment_sufficient {
eprintln!("Insufficient payment");
std::process::exit(1);
}Viewing Your Earnings#
Via Dashboard#
- Go to /earnings
- Connect your NEAR wallet
- View your accumulated balance and total earned
- See detailed history of all payments received
What You Can See#
| Field | Description |
|---|---|
| Current Balance | Amount available to withdraw (total earned minus withdrawn) |
| Total Earned | Lifetime earnings from all API calls (for statistics) |
| Payment History | Individual payments with timestamps, caller info, and amounts |
| Per-Project Stats | Breakdown of earnings by project |
Withdrawing Earnings#
Withdraw your accumulated earnings to receive stablecoins (USDT/USDC) to your NEAR wallet:
- Go to /earnings
- Click "Withdraw"
- Enter amount (or withdraw full balance)
- Sign the transaction
- Stablecoins are transferred to your connected wallet
Minimum withdrawal: $1.00 (1,000,000 micro-units). Smaller amounts can accumulate until the minimum is reached.
Pricing Strategies#
Tiered Pricing#
Offer different functionality levels based on payment amount:
| Tier | Payment | X-Attached-Deposit | Features |
|---|---|---|---|
| Free | $0.00 | 0 | Limited preview, rate limited |
| Basic | $0.01 | 10000 | Standard functionality |
| Premium | $0.10 | 100000 | Enhanced features, priority |
| Enterprise | $1.00 | 1000000 | Full features, extended limits |
Per-Operation Pricing#
Charge based on what the user requests:
#[derive(Deserialize)]
struct Input {
operation: String,
data: serde_json::Value,
}
fn main() {
let input: Input = serde_json::from_reader(std::io::stdin()).unwrap();
let usd_payment = get_usd_payment();
// Different operations have different prices
let required_payment = match input.operation.as_str() {
"simple_query" => 5_000, // $0.005
"data_analysis" => 50_000, // $0.05
"ai_generation" => 200_000, // $0.20
"batch_processing" => 500_000, // $0.50
_ => {
eprintln!("Unknown operation");
std::process::exit(1);
}
};
if usd_payment < required_payment {
let price = required_payment as f64 / 1_000_000.0;
eprintln!(
"Operation '{}' requires {} USD. Set X-Attached-Deposit: {}",
input.operation, price, required_payment
);
std::process::exit(1);
}
// Execute the paid operation
execute_operation(&input);
}Usage-Based Pricing#
Calculate payment based on input size or complexity:
// Price per 1000 characters of input
const PRICE_PER_1K_CHARS: u64 = 1_000; // $0.001
let input_text: String = /* read from stdin */;
let input_len = input_text.len() as u64;
let required_payment = (input_len / 1000 + 1) * PRICE_PER_1K_CHARS;
let usd_payment = get_usd_payment();
if usd_payment < required_payment {
let price = required_payment as f64 / 1_000_000.0;
eprintln!("Input of {} chars requires {} USD", input_len, price);
std::process::exit(1);
}Best Practices#
1. Document your pricing clearly
Include pricing info in your project description and API documentation. Tell users exactly what X-Attached-Deposit values to use.
2. Provide helpful error messages
When payment is insufficient, tell users the exact amount needed and the header value to set.
3. Offer a free tier
Let users try your project with limited functionality before paying. This builds trust and increases conversions.
4. Be consistent across modes
If you support both NEAR transactions and HTTPS API, offer equivalent pricing in both modes (convert between NEAR and USD appropriately).
5. Monitor your earnings
Regularly check the earnings dashboard to understand usage patterns and optimize your pricing strategy.
How Earnings are Tracked#
Earnings data is stored in the coordinator database:
| Table | Purpose |
|---|---|
| project_owner_earnings | Current balance and total earned per project owner |
| payment_key_usage | Individual payment records with attached_deposit, call_id, timestamp |
| project_owner_withdrawals | History of withdrawals with transaction hashes |
When an API call completes, the coordinator atomically:
- Records the payment in
payment_key_usage - Updates
project_owner_earnings.balanceandtotal_earned - Deducts from user's Payment Key balance
Related Documentation
- HTTPS API - Full API reference with X-Attached-Deposit
- Payment Keys - How users fund their API access
- Web2 Integration - Complete project monetization setup
- Building OutLayer App - Environment variables and payment checking