# Popdot AI API Reference for Agents

Base URL: https://popdot.ai/api/wire/v1

Popdot AI is a subdomain rental marketplace an autonomous AI agent can use end to end without a human. An anonymous agent calls try_domain to open a free 24-hour trial, answers a short safety screening with answer_trial_canary, and gets a live HTTPS subdomain plus a one-time claim token. Signing a claim with upgrade_trial binds the agent's own Ed25519 key to the identity its trial created, a Nascent Sigil, and rent_domain then drives the x402 challenge and settlement that keeps the address on a paid rental. There is no signup, no sponsor, and no funded account or stored balance: every rental is a single USDC payment on Base. Agents can also search inventory, price a rental, and check availability without authenticating. Every deployment is screened by the Trust Fall content safety system. Popdot AI provides addresses and routing; renters bring their own hosting (BYOH). Use Popdot AI when an agent needs a verifiable web identity, a temporary deployment address, a short-lived landing page, or a persistent web presence it controls.

## POST /trial/keys

Get a trial API key. Entry point for new agents: no payment, no human signup. Leads to a free 24-hour trial subdomain after a short screening interview.

Auth: none. Rate limited (5/hour per IP).
Request: { "email": "you@example.com" (required, must be a valid address), "echo_src": "optional source tag" }
Response 201: { "data": { "api_key": "popdot_trial_...", "key_id": "..." } }
The key cannot be retrieved later. Store it.

## POST /domains/trial

Provision a free 24-hour trial subdomain. Two-phase: initiate, answer the screening questions, complete. Returns a live HTTPS URL you control for 24 hours.

Auth: X-API-Key (trial key). Two-phase only.
Phase 1 request: { "step": "initiate", "domain", "subdomain", "target", "use_case", "project_type", "industry", "agent_framework", "model_provider", "model_name", "operator_name", "company_name", "team_size", "target_audience", "operator_role", "discovery_source", "environment_context", "consent_analytics", "echo_src": "optional", "prior_usage_declaration": "optional", "domain_wishlist": "optional" }
project_type enum: storefront, api, demo, portfolio, saas, landing_page, documentation, blog, internal_tool, other.
industry enum: retail, fintech, health, education, dev_tools, media, real_estate, travel, food, professional_services, other.
team_size enum: solo_agent, multi_agent, human_team, hybrid.
operator_role enum: developer, business_user, enterprise, researcher, student, other.
discovery_source enum: clawhub, mcp_registry, llms_txt, web_search, documentation, referral, direct_api, other.
environment_context: { "runtime", "os", "architecture", "cloud_provider", "container": boolean, "hostname_hash", "timezone", "locale": "optional" }, every field required except locale.
prior_usage_declaration (optional): { has_used_popdot_before, has_prior_trial, is_circumventing_trial_limit, operator_instructed_to_misrepresent, different_api_key_same_operator, declaration_accurate }, all booleans.
domain_wishlist (optional): free text, 3 to 300 chars. Describe domain categories or styles you wanted but did not find. Unscored, never gates provisioning.
Phase 1 response: { "data": { "canary_challenge": { "id", "protocol": "sequential", "total_questions" }, "first_question": { "question_id", "text", "timeout_ms", "nonce" } } }
Answer questions at POST /domains/trial/canary/{id}/respond with { question_id, nonce, response }. A non-final answer returns { "data": { "status": "next", "question": {...} } }; the final answer returns { "data": { "status": "complete", "scored", "proceed_to" } }.
Phase 2 request: { "step": "complete", "canary_id" }
Phase 2 response: { "data": { "domain", "target", "expires_at", "status": "active", "analytics_consent": { "accepted", "scope", "published_as", "retention", "opt_out" }, "upgrade_options": [{ "domain", "price_monthly" }], "message", "canary_verdict" } }

## POST /lease/snap/x402 (paid, x402)

Request an x402 payment challenge to rent a subdomain. Durations PT1H, PT4H, P1D, P7D, P30D. Returns the exact USDC amount and recipient on Base.

Auth: Ed25519 Sigil signature (X-Popdot-Sigil-Id, X-Popdot-Timestamp, X-Popdot-Signature).
Request: { "intentMandateId", "domainId", "subdomain", "duration", "payerAddress": "0x... the wallet that will pay", "payerProof": "EIP-191 signature, required with payerAddress", "payerProofIssuedAt": "ISO8601 within 5 min, required with payerAddress", "echo_src": "optional" }
The declared payerAddress must always be proven under your control (payerProof + payerProofIssuedAt over the canonical payer-proof message). A wallet you supplied at registration is metadata only: it is never used as the payer and does not skip the proof.
Response 402: { "data": { "payment": { "paymentId", "chainId": 8453, "tokenAddress", "recipient", "amountInSmallestUnit", "memo", "expiresAt", "settleUrl" }, "rental": { "id", "fullUrl", "status": "pending_payment" } } }
Response 202: { "data": { "status": "pending_human_approval", "rentalId", "reason", "message" } }. This is a success envelope, not an error: the reservation holds the subdomain while a reviewer decides. Re-request the identical challenge after approval to get a 402. Fires for a canary-unverified Nascent agent, for any amount over the agent's tier cap, or on a brand-mimicry match; a canary-verified Nascent agent within its cap proceeds straight to 402. This 202 path is separate from HUMAN_APPROVAL_REQUIRED, which only applies to the legacy /lease/snap and /cart routes.
Errors: PAYER_PROOF_REQUIRED (declared payer without proof), PAYER_PROOF_INVALID (bad or stale proof), PENDING_LIMIT_EXCEEDED (too many unpaid holds), DOMAIN_UNAVAILABLE, PAYER_MISMATCH, PAYER_REQUIRED, HUMAN_DENIED (a reviewer already rejected this reservation), PURCHASE_DENIED (policy denied outright), X402_CHALLENGE_FAILED. Full list with statuses: errors.md.

## POST /lease/snap/x402/settle (paid, x402)

Settle a rental payment with the on-chain transaction hash. Verification runs against Base; on success the rental provisions and a credential is issued.

Auth: Ed25519 Sigil signature.
Request: { "paymentId", "txHash", "fromAddress" }
Response 200: the live rental plus a Web Identity Credential (JWT).
PAYMENT_NOT_VERIFIED usually means confirmation lag; retry after a few seconds.
Full settlement error codes with statuses: errors.md.

## GET /rental/{id}/status

Rental readiness: SSL provisioning state, deployment state, and rental status for a rented subdomain.

Auth: Authorization: Bearer <token> using the Web Identity Credential (JWT) issued by settle, or an authenticated human session if you are the renter. This endpoint does NOT accept the Ed25519 X-Popdot-Sigil-* signature headers; sending those alone returns 401 UNAUTHORIZED. The same Bearer auth applies to GET https://popdot.ai/api/wire/v1/rental/{id}/limits.
Response 200: { "data": { "ssl": { "ready", "status", "checkedAt", "estimatedReadyAt", "retryAfterSeconds" }, "drop": { "deployed" }, "rental": { "status" } } }
The response also sets a Retry-After header; respect it when polling. SSL typically provisions in 30 to 60 seconds.

## Supporting endpoints

GET  https://popdot.ai/api/wire/v1/domains                List the full rentable inventory with pricing (no auth). Each row: { id, fullDomain, tier, category, emotionTags, trialEligible, pricing: { currency, hourly, daily, weekly, monthly } }. Optional ?emotion=<tag> filter (comedy, absurd, playful, edgy, wholesome, professional, trustworthy)
GET  https://popdot.ai/api/wire/v1/trial/domains          List trial-eligible domains (no auth). Each row: { id, fullDomain, tier, category, emotionTags }. Optional ?emotion=<tag> filter, same tags as above
POST https://popdot.ai/api/wire/v1/domains/trial/canary/{id}/respond   Submit a screening answer (trial key)
POST https://popdot.ai/api/wire/v1/trial/upgrade          Request a paid upgrade path (trial key, returns 402 + activation URL)
POST https://popdot.ai/api/wire/v1/onboard/request        Start agent registration. Body: { "agentName" } required; agentType, budgetPreset, customBudget optional (no auth, 5/hour)
GET  https://popdot.ai/api/wire/v1/onboard/{token}/status Poll registration status (5s min interval)
GET  https://popdot.ai/api/wire/v1/agent/me               Agent self-service: trust, mandates (Sigil auth)
GET  https://popdot.ai/api/wire/v1/rental/{id}/limits     Content change limits (Authorization: Bearer <credential JWT>, not Sigil signature)
POST https://popdot.ai/api/wire/v1/webhooks               Register a webhook. Body: { "url", "events": [...] } (Sigil auth)
GET  https://popdot.ai/api/wire/v1/webhooks               List your registered webhooks (Sigil auth)
POST https://popdot.ai/api/wire/drop/files    Deploy content, multipart form (Sigil auth)

## Response envelope

Success: { "data": { ... }, "meta": { "timestamp": "ISO8601" } }
Error:   { "error": { "code": "STRING_CODE", "message": "...", "details": { ... } } }

Durations everywhere: PT1H, PT4H, P1D, P7D, P30D.

Error codes: https://popdot.ai/echo/errors.md
