Connector platform
Integration docs
Everything you need to send a draft listing to Zero to Hundred — a prefill URL for web platforms, a stable REST API for custom backends, OAuth 2.1 + PKCE for partner apps, and an official MCP server for AI coding tools. Connector callers create drafts; the Builder reviews and publishes in the wizard.
Overview
The connector platform is three layers. Pick the one that fits how your platform works — they all land in the same place: a status="draft" listing the Builder finishes in the Zero to Hundred wizard.
Prefill URL
Web platforms (no backend)
Build one link, drop it in your Share menu. No SDK, no keys. The Builder signs into Zero to Hundred mid-flow and the wizard mounts pre-filled.
REST API
Custom backends
POST a draft with a bearer token. Personal Access Tokens for individual Builders, OAuth 2.1 + PKCE for partner apps acting on a Builder's behalf.
MCP server
AI coding tools
One MCP server reaches Claude Code, Cursor, Windsurf, Continue, and Cline. The agent calls a tool; a draft appears.
Prefill URL
The lightest integration: a single link. No auth, no SDK. Add ?prefill=connector to the wizard URL and pass the fields you know as query params. The Builder lands there, signs in (Clerk preserves the query string), and the wizard opens pre-filled. Attribute the funnel with source=<your-platform> — every draft created this way lands with created_via set to your name.
https://zerotohundred.ai/listings/new?prefill=connector &source=lovable &title=My%20Cool%20App &shortDescription=Drop-in%20analytics%20for%20indie%20apps &demoUrl=https%3A%2F%2Fmycoolapp.lovable.app &repoUrl=https%3A%2F%2Fgithub.com%2Falice%2Fmycoolapp &techStack=nextjs,supabase,tailwind &type=tool &operatingStatus=ready_to_launch &domains=analytics,productivity
| Field | Type | Notes |
|---|---|---|
| prefill | required | Must be "connector" to activate the prefilled flow. |
| source | string | Funnel attribution → created_via. Capped at 40 chars. |
| title | string | Listing title. Truncated to 200 chars. |
| shortDescription | string | One-line pitch. Truncated to 160 chars. |
| longDescription | string | Full description. |
| demoUrl | url | Accepted only if http(s)-shaped. |
| repoUrl | url | GitHub repo → sourceRepoUrl. Note: the param is repoUrl, not sourceRepoUrl. |
| techStack | csv | Comma-separated, max 10. |
| askingPrice | number | Digits with up to 2 decimals. |
| type | enum | One of the 9 listing types (see Field values). |
| operatingStatus | enum | One of the 3 operating statuses. |
| domains | csv | Comma-separated, max 5. |
Everything is re-validated server-side — the prefill only seeds the form; the Builder confirms before anything is written. A ready-to-drop React button for a Share menu lives in the partner kit.
Authentication
The REST API takes a bearer token in the Authorization header. Cookies are never accepted on /api/v1/*. There are two token types:
Authorization: Bearer z2h_pat_<prefix>_<secret> # Personal Access Token Authorization: Bearer z2h_oat_<secret> # OAuth access token (1h TTL)
- Personal Access Token — a Builder mints one at /account/developers. Best for individual Builders and CLI/agent use.
- OAuth access token — a partner app obtains one via the OAuth 2.1 + PKCE flow to act on a Builder's behalf.
Scopes: listings:write, listings:read, and listings:publish(reserved — publish needs the async source scan and isn't grantable at v1).
Failure semantics: a missing or unresolvable token returns a uniform 401 { "error": "Unauthorized" } (with WWW-Authenticate: Bearer) — the same response for missing/invalid/expired/revoked, so a token can't be probed. A valid token missing the required scope returns 403 { "error": "Forbidden", "reason": "missing_scope:..." }.
REST API
Base path https://zerotohundred.ai/api/v1. Every response carries CORS headers (Access-Control-Allow-Origin: *, methods GET, POST, PATCH, OPTIONS, and Expose-Headers: Retry-After so cross-origin clients can read the rate-limit backoff). OPTIONS preflight returns 204. Connector callers only ever create draft rows — public reads never see them.
Create a draft listing. Scope listings:write. Rate limit 10 requests / 60s, keyed per token.
curl -X POST https://zerotohundred.ai/api/v1/listings \
-H "Authorization: Bearer z2h_pat_..." \
-H "Content-Type: application/json" \
-d '{
"title": "My SaaS",
"shortDescription": "One-line pitch",
"longDescription": "What it does, in ~100 words.",
"type": "micro_saas",
"operatingStatus": "ready_to_launch",
"domains": ["analytics", "productivity"],
"askingPrice": 199,
"demoUrl": "https://example.com",
"sourceRepoUrl": "https://github.com/me/myapp",
"techStack": ["nextjs", "tailwind"],
"source": "api"
}'Body
| Field | Type | Notes |
|---|---|---|
| title | string · required | 3–200 chars. |
| shortDescription | string · required | ≤ 160 chars. |
| longDescription | string · required | ≥ 20 chars. |
| type | enum · required | See Field values. |
| operatingStatus | enum · required | See Field values. |
| domains | string[] | Defaults []. |
| askingPrice | number | Non-negative. |
| demoUrl | url | http/https only. |
| thumbnailUrl | url | http/https only. |
| imageUrls | url[] | Max 5, each http/https. |
| techStack | string[] | Defaults []. |
| testEnvUrl / testEnvInstructions | string | Optional reviewer test env. |
| testEnvCredentials | string | Encrypted at rest; never returned. |
| sourceCodeIncluded | boolean | Defaults true. |
| sourceRepoUrl | url | github.com/<owner>/<repo>; owner must match your connected GitHub handle. |
| source | string | Funnel attribution → created_via (1–40 chars). |
201 Created
{
"id": "...", "slug": "...", "status": "draft",
"createdAt": "<ISO8601>",
"editUrl": "https://zerotohundred.ai/listings/<slug>/edit",
"publishUrl": "https://zerotohundred.ai/listings/<slug>/edit?step=review"
}Errors: 400 (invalid JSON, Zod validation with issues[], bad URL, or GitHub-ownership mismatch), 401/403, 429, 500.
List your own listings, newest first. Scope listings:read. No rate limit. Keyset pagination: pass limit (default 25, max 100) and the opaque cursor from the previous page.
GET https://zerotohundred.ai/api/v1/listings?limit=25&cursor=<opaque>
200 OK
{
"items": [
{ "id","slug","title","status","type","operatingStatus",
"askingPrice","demoUrl","thumbnailUrl","createdVia",
"createdAt","updatedAt","editUrl","publishUrl" }
],
"nextCursor": "<opaque>|null"
}nextCursoris non-null only when more rows exist past this page — stop when it's null.
Read one listing you own. Scope listings:read. A slug you don't own returns 404 (anti-probe — never 403). Returns the full object; testEnvCredentials is never included.
Update a draft. Scope listings:write. Rate limit 10 / 60s per token. Drafts only — a non-draft listing returns 409. All body fields are optional; unknown keys are rejected. Send a field as null to clear it (e.g. sourceRepoUrl: null clears the repo).
PATCH https://zerotohundred.ai/api/v1/listings/<slug>
{ "askingPrice": 149, "techStack": ["nextjs","trpc"] }
200 OK
{ "id","slug","status":"draft","editUrl","publishUrl" }OAuth 2.1 + PKCE
For partner apps acting on a Builder's behalf. Authorization-code + PKCE only — no implicit flow, no password grant. Register an app to get a client_id; choose public (PKCE-only, no secret — for browser/CLI/native clients) or confidential (a client_secret you keep server-side).
1 · Authorize
Send the Builder to the consent screen. This is a page route at /oauth/authorize (not under /api/v1). PKCE is required — a request without code_challenge is rejected.
GET https://zerotohundred.ai/oauth/authorize ?client_id=<your-client-id> &redirect_uri=<exact-registered-uri> &response_type=code &scope=listings:write%20listings:read &state=<csrf-token> &code_challenge=<base64url(sha256(verifier))> &code_challenge_method=S256 → redirects to: <redirect_uri>?code=<code>&state=<state> (or ?error=access_denied&state=<state> on deny)
redirect_uri must match a registered URI exactly (no wildcards). The auth code is single-use and expires in 10 minutes.
2 · Exchange the code for tokens
POST /api/v1/oauth/token (accepts form or JSON; rate-limited 10 / 60s per client).
POST https://zerotohundred.ai/api/v1/oauth/token
grant_type=authorization_code
&client_id=<your-client-id>
&code=<code>
&redirect_uri=<same-as-authorize>
&code_verifier=<pkce-verifier>
# confidential clients ALSO send &client_secret=...
# public clients MUST omit client_secret
200 OK
{ "access_token": "z2h_oat_...", "refresh_token": "...",
"token_type": "Bearer", "expires_in": 3600,
"scope": "listings:read listings:write" }A public client that sends a client_secret is rejected (no silent downgrade); a confidential client that omits it is rejected. Errors use RFC 6749 shape: invalid_request, invalid_client (401), invalid_grant, unsupported_grant_type.
3 · Refresh & revoke
Refresh with grant_type=refresh_token — refresh tokens rotate: the old one is revoked and a new pair issued. Replaying a revoked refresh token revokes every live grant for that Builder + app (chain-compromise detection). Revoke any token (access or refresh) via POST /api/v1/oauth/revoke with token=... — it returns 200 { "ok": true } unconditionally (RFC 7009).
MCP server
The official MCP server, @zerotohundred/mcp-server, reaches every MCP-capable AI coding tool from one package. It reads a Personal Access Token from Z2H_API_KEY and auto-detects the calling client (Claude Code, Cursor, Windsurf, Continue, Cline) for source attribution. If Z2H_API_KEY is unset, every tool returns a one-time setup link instead of calling the API.
Claude Code
claude mcp add z2h \ --env Z2H_API_KEY=YOUR_TOKEN \ -- npx -y @zerotohundred/mcp-server
Cursor / Windsurf / Continue (mcp.json)
{
"mcpServers": {
"z2h": {
"command": "npx",
"args": ["-y", "@zerotohundred/mcp-server"],
"env": { "Z2H_API_KEY": "YOUR_TOKEN" }
}
}
}Tools
| Tool | Does | Calls |
|---|---|---|
| z2h_create_draft_listing | Create a draft | POST /api/v1/listings |
| z2h_list_my_listings | List your listings | GET /api/v1/listings |
| z2h_get_listing | Read one by slug | GET /api/v1/listings/{slug} |
| z2h_update_draft_listing | Update a draft | PATCH /api/v1/listings/{slug} |
There is no publish tool — publishing runs an async source scan and stays in the wizard, where the Builder reviews the draft first.
Field values
type — 9 values
full_product · micro_saas · tool · template · ai_agent component · bot · extension · automation
operatingStatus — 3 values
ready_to_launch · live · live_with_revenue
domains — 29 values (kebab-case)
ai · analytics · art-design · conference · developer-tools e-commerce-marketplace · education · entertainment · environmental fashion · finance · food · gaming · graphic-design · health interior-design · marketing · membership · non-profit · political productivity · real-estate · religious · services · social technology · travel · wedding · other
mode is always license (pay once, own forever), and connector-created listings always start as draft.
Ready to build?
Generate a Personal Access Token to call the REST API or wire up the MCP server, or drop a prefill-URL button in your Share menu — no keys needed.