WaitSpin public contract

WaitSpin API And Agent Docs

This is the public contract for implemented WaitSpin routes and guarded release-candidate surfaces. It intentionally excludes launch-blocked capabilities.

Last updated: June 17, 2026. The public launch surface is CLI, REST API, verified publisher surfaces for VS Code, Claude Code, MiMo Code, OpenCode, and Grok Code CLI, public market, and guarded wallet/ledger/Connect/payout routes.

Base URLs And Auth

Public API host: https://api.waitspin.com. API discovery is available at https://api.waitspin.com/v1. Public launch host: https://waitspin.com. Authenticated routes use Authorization: Bearer wts_live_.... Campaign creation also requires an Idempotency-Key UUID.

The canonical REST API contract is OpenAPI-backed: /openapi/waitspin-api.openapi.json. Code routes and public docs must stay in parity with that spec.

Agent markdown is available at https://waitspin.com/.well-known/agents.md and https://waitspin.com/waitspin/agents.md. It is scoped to the shipped route allowlist, including verified publisher targets, and excludes deferred launch claims.

Public client source and trust-boundary docs are published at github.com/citedy/waitspin and summarized at /waitspin/trust.

VS Code Marketplace provenance is published as JSON at /provenance/waitspin-vscode.json. It records the public source repository, extension version, release source commit, Marketplace URL, npm package version, VSIX filename, and VSIX SHA256 without committing the VSIX binary.

Authenticated routes are API-key rate limited at 60/minand count against monthly API-call quota except publisher serve/impression polling. GET /v1/market is IP limited at 60/min.

Use control keys for advertiser, Connect, and payout commands. Use publisher-extension keys only for extension registration, serve polling, impression events, and read-only wallet visibility.

Implemented Routes And Guarded Surfaces

MethodPathAuthPurpose
GET/v1noneRead API discovery metadata with docs and OpenAPI URLs.
POST/v1/keys/requestnoneRequest an email verification code.
POST/v1/keys/verifynoneVerify code and receive a wts_live_ key.
GET/v1/marketnoneRead active public campaign leaderboard.
POST/v1/campaignscampaigns:writeCreate a draft campaign and pending block purchase. Requires Idempotency-Key.
GET/v1/campaignscampaigns:readList campaigns for the API key account.
POST/v1/blocks/checkoutblocks:purchaseCreate or reuse a Stripe Checkout URL for a pending campaign.
POST/v1/publishers/registerpublishers:writeRegister a publisher install ID for a supported publisher target.
POST/v1/serve/nextserve:readReturn the next sponsored message, or 204 when no inventory is available.
POST/v1/events/impressionevents:writeRecord a billable impression after the server-side visible interval.
GET/v1/wallet/statuswallet:readRead publisher balance, payout eligibility, and Connect status.
POST/v1/wallet/connectconnect:manageCreate or refresh a Stripe Express onboarding link.
GET/v1/wallet/ledgerwallet:readRead publisher delivery, refund-reversal, and dispute-hold delivery-ledger rows.
POST/v1/wallet/payoutsconnect:managePreview or execute a guarded idempotent publisher payout.
POST/api/waitspin/webhookStripe signatureStripe Checkout activation, refund/dispute accounting, and Connect account sync.

Request And Response Shapes

GET /v1
response: { "name": "WaitSpin REST API", "version": "v1", "api_base_url": "https://api.waitspin.com/v1", "docs_url": "https://waitspin.com/docs", "openapi_url": "https://waitspin.com/openapi/waitspin-api.openapi.json", "routes": { "discovery": ["/v1"], "control": ["/v1/market"], "webhooks": ["/api/waitspin/webhook"] } }

POST /v1/keys/request
request:  { "email": "[email protected]", "intended_use": "optional" }
response: { "ok": true, "expires_in_seconds": 900, "delivery": "email" }

POST /v1/keys/verify
request:  { "email": "[email protected]", "code": "123456" }
control response: { "account_id": "wacc_...", "api_key": "wts_live_...", "scopes": ["campaigns:write","campaigns:read","blocks:purchase","serve:read","events:write","wallet:read","connect:manage","analytics:read","publishers:write"], "trust_level": "email_verified" }
publisher-extension response: { "account_id": "wacc_...", "api_key": "wts_live_...", "scopes": ["publishers:write","serve:read","events:write","wallet:read"], "trust_level": "email_verified" }

GET /v1/market
response: { "campaigns": [{ "campaign_id": "wcamp_...", "ad_line": "...", "brand_name": null, "bid_cpm_micros": 1000000, "impressions_served": 0, "status": "active" }] }

POST /v1/campaigns
headers:  Authorization: Bearer wts_live_...; Idempotency-Key: <v4-uuid>
request:  { "ad_line": "...", "destination_url": "https://example.com", "brand_name": "Example", "price_per_block_cents": 500, "blocks": 1 }
response: { "campaign_id": "wcamp_...", "block_purchase_id": "wbp_...", "status": "draft", "blocks": 1, "price_per_block_cents": 500 }

GET /v1/campaigns
response: { "campaigns": [{ "id": "wcamp_...", "ad_line": "...", "status": "draft", "blocks_purchased": 1, "units_remaining": 1000000 }] }

POST /v1/blocks/checkout
request:  { "campaign_id": "wcamp_..." }
response: { "checkout_url": "https://checkout.stripe.com/...", "block_purchase_id": "wbp_..." }

POST /v1/publishers/register
request:  { "install_id": "wins_...", "target": "status-bar-fallback" | "claude-code" | "mimocode" | "opencode" | "grok" }
response: { "publisher_id": "wpub_...", "install_id": "wins_...", "target": "status-bar-fallback" | "claude-code" | "mimocode" | "opencode" | "grok" }

POST /v1/serve/next
request:  { "install_id": "wins_...", "slot_id": "optional" }
response: 204, or { "serve_id": "wss_...", "creative": { "line": "...", "destination_url": "https://example.com", "campaign_id": "wcamp_..." }, "min_visible_ms": 5000, "expires_at": "...", "serve_receipt": "wtsr_v1..." }

POST /v1/events/impression
request:  { "serve_id": "wss_...", "serve_receipt": "wtsr_v1...", "install_id": "wins_...", "visible_ms": 5000 }
response: { "ok": true, "billed_micro_units": 5000 }

GET /v1/wallet/status
response: { "account_id": "wacc_...", "balance": { "available_micro_units": 0, "maturing_micro_units": 0, "held_micro_units": 0, "reversed_micro_units": 0, "reversal_debt_micro_units": 0, "paid_micro_units": 0, "lifetime_earned_micro_units": 0, "pending_payout_micro_units": 0 }, "connect": { "connected": false, "stripe_account_id": null, "payouts_enabled": false, "details_submitted": false }, "payout_policy": { "min_payout_cents": 1000, "cadence_days": 7, "currency": "eur", "earning_maturity_hours": 72, "eligible": false, "transfer_cents": 0, "next_eligible_at": null, "blocked_reasons": ["connect_account_missing"] }, "payout_hold": { "active": false, "reason": null, "created_at": null }, "publisher_trust": { "level": 1, "base_level": 1, "max_level": 10, "status": "warming", "clean_days": 0, "normal_cap_share_bps": 1000, "first_billable_at": null, "next_level_at": null, "reasons": [], "paid_supply_allowed": true, "paid_supply_blocked_reasons": [] } }

POST /v1/wallet/connect
response: { "stripe_account_id": "acct_...", "onboarding_url": "https://connect.stripe.com/...", "payouts_enabled": false, "details_submitted": false }

GET /v1/wallet/ledger?limit=50
response: { "entries": [{ "id": "wled_...", "event_type": "impression", "block_purchase_id": "wbp_...", "source_ledger_id": null, "stripe_event_id": null, "gross_micro_units": 5000, "publisher_micro_units": 3000, "platform_micro_units": 2000, "earning_matures_at": "...", "earning_matured_at": null, "created_at": "..." }] }

POST /v1/wallet/payouts
headers:  Authorization: Bearer wts_live_...; Idempotency-Key required for non-dry-run
request:  { "dry_run": true, "confirm_test_transfer": false }
response:
{
  "ok": true,
  "dry_run": true,
  "amount_micro_units": 0,
  "amount_cents": 0,
  "currency": "eur",
  "eligible": false,
  "blocked_reasons": ["connect_account_missing", "earnings_maturing", "balance_below_minimum"],
  "min_payout_cents": 1000,
  "cadence_days": 7,
  "next_eligible_at": null,
  "payouts_enabled": false,
  "details_submitted": false
}

POST /api/waitspin/webhook
auth:     Stripe signature over raw body
events:   checkout.session.completed, checkout.session.async_payment_succeeded, charge.refunded, charge.dispute.created, account.updated
response: { "ok": true }

POST /v1/campaigns idempotency keys expire after 24 hours. Reusing a key with the same payload replays the stored response; reusing it with a different payload returns a conflict response (409).

Public publisher targets are status-bar-fallback, installed from VS Code Marketplace with code --install-extension waitspin.waitspin-vscode and connected inside VS Code with WaitSpin: Connect publisher, claude-code, installed by waitspin claude-code install --compose-existing, mimocode, installed by waitspin mimocode install, and opencode, installed by waitspin opencode install, and grok, installed by waitspin grok install. Claude Code support uses the official statusLine.command path; MiMo Code uses a managed shell hook; OpenCode uses a managed TUI plugin entry; Grok Code CLI uses a managed text-asset footer patch with hash-backed restore. Cline VS Code extension installs are covered by the WaitSpin VS Code Marketplace extension; standalone Cline CLI awaits official statusline/plugin support. Native spinner patch targets remain deferred.

Legal And Payment Disclosures

Review the Terms and Privacy notices before install, Checkout, or publisher registration. Unused prepaid block handling is support-reviewed; no automated account-credit balance, redemption flow, or self-serve cash refund request flow is shipped.

VS Code Publisher Setup

The first-class VS Code publisher path is the WaitSpin VS Code Marketplace extension. Install it with code --install-extension waitspin.waitspin-vscode, then run WaitSpin: Connect publisher inside VS Code.

The extension requests or accepts a publisher-extension key, registers the VS Code install through POST /v1/publishers/register, stores the key in VS Code SecretStorage, and starts wallet/sponsor polling against https://api.waitspin.com.

CLI setup remains the advanced fallback: waitspin extension install --target vscode --api-key PASTE_PUBLISHER_EXTENSION_KEY.

Agent Quick Start

# Verify the published package before using this as release evidence.
npm view waitspin version
npx --yes waitspin init --email [email protected] --key-profile control
export WAITSPIN_API_KEY=PASTE_CONTROL_KEY
waitspin bid create --line "Your ad" --url https://example.com --price-per-block 500 --blocks 1
waitspin bid checkout CAMPAIGN_ID
npx --yes waitspin init --email [email protected] --key-profile publisher-extension

# Advanced agent install for detected supported targets
waitspin install --all --dry-run --api-key PASTE_PUBLISHER_EXTENSION_KEY --compose-existing
waitspin install --all --api-key PASTE_PUBLISHER_EXTENSION_KEY --compose-existing
waitspin status --all

# VS Code publisher extension
# Marketplace: https://marketplace.visualstudio.com/items?itemName=waitspin.waitspin-vscode
code --install-extension waitspin.waitspin-vscode
# Then run "WaitSpin: Connect publisher" in VS Code.
# CLI fallback:
waitspin extension install --target vscode --api-key PASTE_PUBLISHER_EXTENSION_KEY
waitspin extension status --target vscode

# Claude Code statusline
waitspin claude-code install --api-key PASTE_PUBLISHER_EXTENSION_KEY --compose-existing
waitspin claude-code status

# MiMo Code shell hook
waitspin mimocode install --api-key PASTE_PUBLISHER_EXTENSION_KEY
waitspin mimocode status

# OpenCode TUI plugin slot
waitspin opencode install --api-key PASTE_PUBLISHER_EXTENSION_KEY
waitspin opencode status

# Grok Code CLI footer
waitspin grok install --api-key PASTE_PUBLISHER_EXTENSION_KEY
waitspin grok status

Explicit target commands remain the canonical debug path. waitspin install --all is an advanced agent command that installs only detected supported targets and reports structuredinstalled, would_install, skipped_not_detected, skipped_conflict, and failed_rollback arrays. Use a publisher-extension key for polling/events. The VS Code extension can connect a publisher inside VS Code and stores keys in SecretStorage; the Claude Code installer stores managed runtime state under ~/.waitspin and does not write the key into Claude settings.

Publisher-extension keys are valid only for publisher registration, serve polling, impression events, and read-only wallet status/ledger. They cannot create campaigns, start Checkout, manage Connect, or execute payouts.

Response And Error Contract

Successful JSON responses include route-specific fields and no cache storage for authenticated control routes. Empty inventory returns 204 No Content. Client errors use standard HTTP status codes such as 400, 401, 403, 409, 422, and 429 with a JSON error body; rate limits may include Retry-After.

Billed impression delivery uses a 60% publisher share and 40% platform share. Stripe processing fees are absorbed from the platform share unless the payment policy changes.

Not In The Public Contract Yet

Native spinner patches beyond supported status surfaces, click billing, account-credit redemption, cash refund self-service, live payout transfers without explicit operator flags and deployed evidence, geo targeting, and house ads are withheld from the public contract until implementation and launch evidence are complete.

Legal And Security

WaitSpin uses hashed API-key storage, host isolation, trusted-edge checks, rate limits, and audit logging. Keep API keys secret and do not commit VS Code, Claude Code, or environment files containing credentials.