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#

  1. User makes API call with X-Attached-Deposit: 100000 ($0.10)
  2. Payment is charged immediately from user's Payment Key balance
  3. Your WASM receives USD_PAYMENT="100000" environment variable
  4. On completion, payment is added to your project_owner_earnings
  5. 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#

  1. Go to /earnings
  2. Connect your NEAR wallet
  3. View your accumulated balance and total earned
  4. See detailed history of all payments received

What You Can See#

FieldDescription
Current BalanceAmount available to withdraw (total earned minus withdrawn)
Total EarnedLifetime earnings from all API calls (for statistics)
Payment HistoryIndividual payments with timestamps, caller info, and amounts
Per-Project StatsBreakdown of earnings by project

Withdrawing Earnings#

Withdraw your accumulated earnings to receive stablecoins (USDT/USDC) to your NEAR wallet:

  1. Go to /earnings
  2. Click "Withdraw"
  3. Enter amount (or withdraw full balance)
  4. Sign the transaction
  5. 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:

TierPaymentX-Attached-DepositFeatures
Free$0.000Limited preview, rate limited
Basic$0.0110000Standard functionality
Premium$0.10100000Enhanced features, priority
Enterprise$1.001000000Full 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:

TablePurpose
project_owner_earningsCurrent balance and total earned per project owner
payment_key_usageIndividual payment records with attached_deposit, call_id, timestamp
project_owner_withdrawalsHistory of withdrawals with transaction hashes

When an API call completes, the coordinator atomically:

  1. Records the payment in payment_key_usage
  2. Updates project_owner_earnings.balance and total_earned
  3. Deducts from user's Payment Key balance

Related Documentation