Agent Quickstart
Medialane is built to be consumed by autonomous AI agents. Wallet keypair identity, on-chain payments, and machine-readable 402 billing make it a natural fit for agent workflows.
Why agents work here
Traditional API platforms require email, OAuth, or credit-card billing — all designed for humans. Medialane uses permissionless primitives instead:
- Identity — a Starknet wallet keypair. No email, no OAuth provider, no third-party dependency.
- Auth — Sign-In with Starknet (SIWS). Sign a typed-data challenge, receive a short-lived JWT.
- Billing — USDC on Starknet. Deposit on-chain, credits settle within ~2 minutes.
- Access gate — 500 MDLN minimum in the agent wallet to provision an API key. Tokens stay in the wallet.
- Credit exhaustion — machine-readable
402withX-Credits-Remainingheader, not a human-facing error page.
SIWS Authentication
Agents authenticate the same way a browser wallet does — by signing a typed-data challenge. Use starknet.js with a local keypair:
1. Fetch challenge
import { RpcProvider, Account, ec, stark } from "starknet";
const address = process.env.AGENT_WALLET_ADDRESS!;
const privateKey = process.env.AGENT_PRIVATE_KEY!;
const res = await fetch(
`https://portal.medialane.io/api/auth/challenge?address=${address}`
);
const { nonce } = await res.json();2. Sign the typed-data challenge
const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL });
const account = new Account(provider, address, privateKey);
// The same typed-data domain the portal uses
const typedData = {
types: {
StarkNetDomain: [
{ name: "name", type: "felt" },
{ name: "version", type: "felt" },
{ name: "chainId", type: "felt" },
],
Message: [
{ name: "nonce", type: "felt" },
{ name: "address", type: "felt" },
],
},
primaryType: "Message",
domain: { name: "Medialane Portal", version: "1", chainId: "SN_MAIN" },
message: { nonce, address },
};
const signature = await account.signMessage(typedData);3. Exchange for JWT
const verifyRes = await fetch("https://portal.medialane.io/api/auth/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address, nonce, signature }),
credentials: "include", // stores auth-token + auth-refresh cookies
});
// JWT is set as an HttpOnly cookie automatically.
// For headless agents, extract from Set-Cookie if needed.Get an API Key
After authenticating, provision your wallet and create an API key via the portal API. Keys are tied to the wallet address, not an email account.
// Provision wallet (idempotent — safe to call on every boot)
await fetch("https://portal.medialane.io/api/portal/provision", {
method: "POST",
credentials: "include",
});
// Create an API key
const keyRes = await fetch("https://portal.medialane.io/api/portal/keys", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "agent-key" }),
credentials: "include",
});
const { key } = await keyRes.json();
// store key securely — shown only onceCredit Billing — Handling 402
Every response includes an X-Credits-Remaining header. When credits reach zero the API returns 402 Payment Required. Detect and handle it programmatically:
async function apiCall(endpoint: string) {
const res = await fetch(
`https://medialane-backend-production.up.railway.app${endpoint}`,
{ headers: { "x-api-key": process.env.ML_API_KEY! } }
);
const remaining = Number(res.headers.get("X-Credits-Remaining") ?? 0);
if (remaining < 10) {
console.warn(`Low credits: ${remaining} remaining`);
}
if (res.status === 402) {
// Credits exhausted — trigger top-up before retrying
await topUpCredits();
return apiCall(endpoint); // retry once
}
return res.json();
}Autonomous USDC Top-up
Agents can top up credits autonomously by sending USDC to the treasury address on Starknet. Credits appear within ~2 minutes (one Vercel cron cycle).
import { Account, RpcProvider, Contract } from "starknet";
const USDC_CONTRACT = "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
const TREASURY = process.env.NEXT_PUBLIC_TREASURY_ADDRESS!;
async function topUpCredits(usdcAmount = 5) {
const provider = new RpcProvider({ nodeUrl: process.env.STARKNET_RPC_URL });
const account = new Account(provider, process.env.AGENT_WALLET_ADDRESS!, process.env.AGENT_PRIVATE_KEY!);
// USDC has 6 decimals
const amount = BigInt(usdcAmount * 1_000_000);
await account.execute([{
contractAddress: USDC_CONTRACT,
entrypoint: "transfer",
calldata: [TREASURY, amount.toString(), "0"],
}]);
// Poll until credits arrive (typically < 2 min)
await waitForCredits();
}
async function waitForCredits(retries = 20, delayMs = 7_000) {
for (let i = 0; i < retries; i++) {
await new Promise(r => setTimeout(r, delayMs));
const res = await fetch("https://portal.medialane.io/api/credits/balance", {
credentials: "include",
});
const { balance } = await res.json();
if (balance > 0) return;
}
throw new Error("Credits did not appear after 2 minutes");
}MDLN Token Multipliers
If your agent wallet holds MDLN tokens, the multiplier is applied automatically at deposit time — no configuration required. The on-chain balance is read at the moment the deposit transaction is detected.
Learn more about MDLN at medialane.org. Hold MDLN in the same wallet you use as your agent identity.
Next Steps
- API Reference — all available endpoints
- SDK Docs — typed TypeScript client (@medialane/sdk)
- Integrate — credit costs and MDLN tier details