Documentation
Guides and reference for OutLayer verifiable compute and agent custody.
Docs navigation
HTTPS API
Call OutLayer projects via simple HTTP requests without NEAR transactions. Authentication uses Payment Keys with prepaid USD stablecoin balance.
Overview#
NEAR Transactions
- Payment: NEAR tokens
- Latency: ~2-3 seconds
- Response: callback to contract
HTTPS API
- Payment: USD stablecoins
- Latency: instant (sync mode)
- Response: HTTP response body
# Simple example
curl -X POST https://api.outlayer.ai/call/alice.near/my-assistant \
-H "X-Payment-Key: bob.near:1:K7xR2mN9pQs5vW3yZ8bF..." \
-H "Content-Type: application/json" \
-d '{"input": {"prompt": "Hello!"}}'Request Format#
Endpoint#
POST https://api.outlayer.ai/call/{project_owner}/{project_name}Example: POST https://api.outlayer.ai/call/alice.near/weather-api
Base URLs (Networks)#
OutLayer runs on two networks. Pick the base URL that matches the network your project is deployed on — the path (/call/..., /wallet/v1/...,/vrf/..., etc.) is identical on both.
| Network | Base URL | Contract |
|---|---|---|
| Mainnet | https://api.outlayer.ai | outlayer.near |
| Testnet | https://testnet-api.outlayer.ai | outlayer.testnet |
Request Headers#
| Header | Required | Default | Description |
|---|---|---|---|
| X-Payment-Key | Yes | - | Format: owner:nonce:secretAuthentication for the API call |
| X-Compute-Limit | No | 10000 | Max compute budget in USD micro-units 10000 = $0.01, 100000 = $0.10 |
| X-Attached-Deposit | No | 0 | Payment to project author in USD micro-units 1000000 = $1.00. Goes to author's earnings. |
| Content-Type | No | application/json | Request body format |
X-Attached-Deposit vs X-Compute-Limit:
X-Compute-Limit- pays for infrastructure (OutLayer), refunded if unusedX-Attached-Deposit- pays the project author, charged immediately
Request Body#
{
"input": { // Required - passed to WASM as stdin
"prompt": "Hello, AI!",
"temperature": 0.7
},
"resource_limits": { // Optional - override project defaults
"max_instructions": 10000000000,
"max_memory_mb": 128,
"max_execution_seconds": 60
},
"secrets_ref": { // Optional - keystore secrets
"profile": "default",
"account_id": "alice.near"
},
"async": false, // Optional - sync (default) or async mode
"version_key": "user/repo@a1b2c3" // Optional - pin to specific version
}| Field | Type | Required | Description |
|---|---|---|---|
| input | object | Yes | Passed to WASM code as JSON via stdin |
| resource_limits | object | No | Override project's default limits |
| secrets_ref | object | No | Reference to keystore secrets. Contains profile (string) and account_id (string). Decrypted secrets are injected as environment variables into WASM. |
| async | boolean | No | false = wait for result, true = return call_id immediately |
| version_key | string | No | Pin to a specific project version. Format: WASM hash or repo@commit. If omitted, uses the project's active version. |
Resource Limits#
| Limit | Default | Maximum | Description |
|---|---|---|---|
| max_instructions | 1 billion | 500 billion | WASM instructions to execute |
| max_memory_mb | 128 MB | 512 MB | Memory available to WASM |
| max_execution_seconds | 60 sec | 180 sec | Wall-clock timeout |
Response Format#
Synchronous Response (default)#
When async: false (default), the request waits until execution completes:
// Success
{
"call_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"output": "Here's the weather forecast for...",
"compute_cost": "45000",
"job_id": 12345,
"attestation_url": "https://app.outlayer.ai/attestations/12345"
}
// Failure
{
"call_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Execution timeout after 60 seconds",
"compute_cost": "100000",
"job_id": 12345
}Asynchronous Response#
When async: true, the request returns immediately with a call_id for polling:
// Initial response
{
"call_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"poll_url": "https://api.outlayer.ai/calls/550e8400-e29b-41d4-a716-446655440000"
}Poll the result using GET /calls/{call_id}:
curl -H "X-Payment-Key: bob.near:1:..." \
https://api.outlayer.ai/calls/550e8400-e29b-41d4-a716-446655440000Response Fields#
| Field | Type | Description |
|---|---|---|
| call_id | UUID | Unique identifier for this API call |
| status | string | "pending", "completed", or "failed" |
| output | any | WASM stdout (only when completed) |
| error | string | Error message (only when failed) |
| compute_cost | string | Actual USD cost in micro-units (e.g., "45000" = $0.045). Charged even on failure. |
| job_id | number | Internal job ID for attestation lookup |
| attestation_url | string | Link to TEE attestation (completed only) |
Environment Variables in WASM#
Your WASM code can detect the execution context and access payment information via environment variables. Values differ between NEAR transactions and HTTPS API calls:
| Variable | NEAR | HTTPS |
|---|---|---|
| OUTLAYER_EXECUTION_TYPE | "NEAR" | "HTTPS" |
| NEAR_NETWORK_ID | "testnet" or "mainnet" | "testnet" or "mainnet" |
| NEAR_SENDER_ID | Transaction signer | Payment Key owner |
| USD_PAYMENT | "0" | X-Attached-Deposit value |
| NEAR_PAYMENT_YOCTO | Attached NEAR | "0" |
| OUTLAYER_CALL_ID | "" | call_id UUID |
| NEAR_TRANSACTION_HASH | Transaction hash | "" |
| NEAR_BLOCK_HEIGHT | Block number | "" |
| NEAR_BLOCK_TIMESTAMP | Block timestamp | "" |
| OUTLAYER_PROJECT_ID | owner/name (same for both) | |
| OUTLAYER_PROJECT_OWNER | Project owner account (e.g., "alice.near") | |
| OUTLAYER_PROJECT_NAME | Project name (may contain "/") | |
Detecting Execution Mode#
let execution_type = std::env::var("OUTLAYER_EXECUTION_TYPE")
.unwrap_or_else(|_| "NEAR".to_string());
match execution_type.as_str() {
"HTTPS" => {
// HTTPS API call
let usd_payment: u64 = std::env::var("USD_PAYMENT")
.unwrap_or_else(|_| "0".to_string())
.parse()
.unwrap_or(0);
let call_id = std::env::var("OUTLAYER_CALL_ID").unwrap_or_default();
let cost = usd_payment as f64 / 1_000_000.0;
println!("HTTPS call {}: paid {} USD", call_id, cost);
}
"NEAR" => {
// NEAR transaction
let near_payment: u128 = std::env::var("NEAR_PAYMENT_YOCTO")
.unwrap_or_else(|_| "0".to_string())
.parse()
.unwrap_or(0);
let tx_hash = std::env::var("NEAR_TRANSACTION_HASH").unwrap_or_default();
println!("NEAR tx {}: paid {} yoctoNEAR", tx_hash, near_payment);
}
_ => {}
}Checking Payment (USD)#
// USD_PAYMENT is in micro-units: 1000000 = $1.00
let usd_payment: u64 = std::env::var("USD_PAYMENT")
.unwrap_or_else(|_| "0".to_string())
.parse()
.unwrap_or(0);
// Require $0.10 minimum for premium features
const MIN_PREMIUM_USD: u64 = 100_000; // $0.10
if usd_payment >= MIN_PREMIUM_USD {
// Premium feature
expensive_ai_analysis();
} else if usd_payment > 0 {
// Basic paid feature
basic_analysis();
} else {
// Free tier
simple_response();
}Using Secrets#
If your WASM code needs API keys or other sensitive data, use Keystore Secrets. Secrets are encrypted and stored on-chain, then decrypted inside TEE at execution time and injected as environment variables.
Pass secrets_ref in the request body to specify which secret profile to use:
curl -X POST https://api.outlayer.ai/call/alice.near/weather-api \
-H "X-Payment-Key: bob.near:1:K7xR2mN9pQs5vW3yZ8bF..." \
-H "Content-Type: application/json" \
-d '{
"input": {"city": "Tokyo"},
"secrets_ref": {
"profile": "default",
"account_id": "alice.near"
}
}'| Field | Type | Description |
|---|---|---|
| profile | string | Secret profile name (e.g., "default", "production"). Configured in the Secrets page. |
| account_id | string | NEAR account that owns the secrets (the one who encrypted them) |
Accessing Secrets in WASM#
Decrypted secrets are available as regular environment variables:
// Secrets are injected as env vars by the TEE worker
let api_key = std::env::var("OPENAI_API_KEY")
.expect("OPENAI_API_KEY secret not set");
let db_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL secret not set");Works the same as NEAR transactions. The secrets_ref field uses the same keystore system as the secrets_ref parameter in request_execution contract calls. If your WASM already uses secrets via NEAR transactions, just pass the same profile in HTTPS calls.
Error Codes#
| Code | Status | Description |
|---|---|---|
| 400 | Bad Request | Invalid request body or headers |
| 401 | Unauthorized | Invalid or missing Payment Key |
| 402 | Payment Required | Insufficient balance on Payment Key |
| 403 | Forbidden | Project not allowed for this key |
| 404 | Not Found | Project does not exist |
| 429 | Too Many Requests | Rate limit exceeded (IP or key) |
| 500 | Internal Error | Server error during execution |
| 504 | Gateway Timeout | Execution timeout (300s max) |
Code Examples#
cURL#
# Basic call
curl -X POST https://api.outlayer.ai/call/alice.near/weather-api \
-H "X-Payment-Key: bob.near:1:K7xR2mN9pQs5vW3yZ8bF..." \
-H "Content-Type: application/json" \
-d '{"input": {"city": "Tokyo"}}'
# With compute limit and author payment
curl -X POST https://api.outlayer.ai/call/alice.near/premium-api \
-H "X-Payment-Key: bob.near:1:K7xR2mN9pQs5vW3yZ8bF..." \
-H "X-Compute-Limit: 500000" \
-H "X-Attached-Deposit: 100000" \
-H "Content-Type: application/json" \
-d '{"input": {"query": "complex analysis"}}'
# Async mode
curl -X POST https://api.outlayer.ai/call/alice.near/long-running \
-H "X-Payment-Key: bob.near:1:K7xR2mN9pQs5vW3yZ8bF..." \
-H "Content-Type: application/json" \
-d '{"input": {...}, "async": true}'JavaScript / TypeScript#
async function callOutLayer(projectId: string, input: object) {
const response = await fetch(`https://api.outlayer.ai/call/${projectId}`, {
method: 'POST',
headers: {
'X-Payment-Key': process.env.OUTLAYER_PAYMENT_KEY!,
'X-Compute-Limit': '100000', // $0.10 max
'X-Attached-Deposit': '50000', // $0.05 to author
'Content-Type': 'application/json',
},
body: JSON.stringify({ input }),
});
if (!response.ok) {
throw new Error(`OutLayer error: ${response.status}`);
}
const result = await response.json();
if (result.status === 'failed') {
throw new Error(`Execution failed: ${result.error}`);
}
const costUsd = Number(result.compute_cost) / 1_000_000;
console.log('Cost:', costUsd, 'USD');
return result.output;
}
// Usage
const weather = await callOutLayer('alice.near/weather-api', { city: 'Tokyo' });Python#
import os
import requests
def call_outlayer(project_id: str, input_data: dict) -> dict:
"""Call an OutLayer project via HTTPS API."""
response = requests.post(
f"https://api.outlayer.ai/call/{project_id}",
headers={
"X-Payment-Key": os.environ["OUTLAYER_PAYMENT_KEY"],
"X-Compute-Limit": "100000", # $0.10 max
"X-Attached-Deposit": "50000", # $0.05 to author
"Content-Type": "application/json",
},
json={"input": input_data},
)
response.raise_for_status()
result = response.json()
if result["status"] == "failed":
raise Exception(f"Execution failed: {result['error']}")
cost = int(result['compute_cost']) / 1_000_000
print(f"Cost: {cost} USD")
return result["output"]
# Usage
weather = call_outlayer("alice.near/weather-api", {"city": "Tokyo"})Pricing#
HTTPS API calls are charged in USD stablecoins based on actual resource consumption:
+ (instructions / 1M) × per_million_instructions_usd
+ execution_time_ms × per_ms_usd
+ compile_time_ms × per_compile_ms_usd
See Pricing & Limits for current rates. Key points:
- compute_cost - charged based on actual resources used
- X-Attached-Deposit - charged immediately, goes to project author
- Minimum: X-Compute-Limit must be at least $0.001 (1000)
- Failures: you still pay for resources used before the failure
Related Documentation
- Payment Keys - Creating and managing Payment Keys
- Earnings - How project authors earn from X-Attached-Deposit
- Secrets (Keystore) - Encrypting and managing secrets for WASM execution
- Web2 Integration - Complete HTTPS integration guide
- TEE Attestation - Verifying execution attestations