BETAFINDS PUBLISHER API ======================= Version 1. Documentation optimized for LLM agents and humans. MENTAL MODEL ------------ An author on BetaFinds has startups (each with a unique slug). For an approved (APPROVED) startup the author can: 1. Publish UPDATES — entries in the "What's new" feed: releases, improvements, fixes, news. Startup subscribers receive an email summary within ~10 minutes after publishing. 2. Create RELEASES — versions with files for Windows/macOS/Android/Linux. Files are uploaded directly to storage via a one-time link (presigned PUT), downloaded by visitors from https://downloads.betafinds.com/{slug}/{version}/{file}. 3. Run a PRELAUNCH: a startup can be published BEFORE the product is ready (stage = PRELAUNCH or BETA, url optional). Visitors join the waitlist; participants' contacts are never disclosed — only aggregated counters and anonymized answers are available. When the product is ready — announce_launch: the stage becomes LAUNCHED, an update is published and the waitlist is notified (once). 4. Read REVIEWS from Google Play / App Store: the platform automatically collects public reviews via the saved app links. Typical agent flow after building a new version of an app: create_release → request_asset_upload (for each file) → PUT the file → complete_asset_upload Typical prelaunch flow: create_startup (stage=PRELAUNCH, no url) → publish_update (progress) → get_waitlist (watch the audience) → announce_launch (when ready) AUTHENTICATION -------------- An API token is created on the https://betafinds.com/profile page (the "API tokens" section). Format: blt_<48 hex chars>. Passed in the header: Authorization: Bearer blt_... All responses are JSON: {"ok": true, "result": ...} or {"ok": false, "error": "..."}. Base URL: https://betafinds.com TWO WAYS TO INTEGRATE --------------------- A) MCP (recommended for Claude and other LLM agents): Endpoint: https://betafinds.com/api/mcp (Streamable HTTP, stateless) Claude Code: claude mcp add betafinds https://betafinds.com/api/mcp --transport http \ --header "Authorization: Bearer blt_YOUR_TOKEN" Tools: create_startup, update_startup, upload_image, list_categories, list_my_startups, publish_update, create_release, request_asset_upload, complete_asset_upload, get_releases, change_startup_stage, announce_launch, get_waitlist, get_store_reviews. B) REST API — methods below. REST METHODS ------------ GET /api/v1/me Verify the token. Returns the owner's id, name, email. GET /api/v1/startups List of your startups with their current store links. result: [{slug, name, status, tagline, url, appStoreUrl, testflightUrl, googlePlayUrl}] Publishing is only possible when status = "APPROVED". GET /api/v1/categories Public list of categories (no token). result: [{slug, name}]. Use slug in create_startup (categorySlug). A category name also works — matching is case-insensitive; if not found, the startup is created without a category (the response includes a warning). POST /api/v1/images Upload an image (logo/screenshot). Body = raw file bytes, the Content-Type header = the image type. Max 2 MB; JPEG/PNG/WebP/GIF. curl -X POST "https://betafinds.com/api/v1/images" -H "$AUTH" \ -H "Content-Type: image/png" --data-binary @icon.png result: {path, url} — use url in create_startup. POST /api/v1/startups Create a new startup (submitted for review). | Field | Type | Req. | Description | |--------------|----------|------|----------------------------------------| | name | string | yes | Name | | tagline | string | yes | Tagline, up to 160 characters | | description | string | yes | Full description, min 50 characters | | url | string | * | Website URL (required for LAUNCHED) | | stage | string | no | PRELAUNCH / BETA / LAUNCHED (default) | | targetLaunchDate | string | no | Estimated launch date (YYYY-MM-DD) | | prelaunchAudience| string | no | Who the product is for (up to 1000) | | prelaunchReady | string | no | What is already done (up to 1000) | | prelaunchPromise | string | no | What subscribers get (up to 1000) | | categorySlug | string | no | Category slug (see https://betafinds.com/categories) | | tags | string[] | no | Tags, up to 6 | | logo | string | no | logo url from POST /api/v1/images | | screenshots | string[] | no | screenshot urls (up to 5) from /images | | appStoreUrl, testflightUrl, googlePlayUrl | string | no | App store links (iOS/Android) | result: {slug, url, status}. Images are accepted only as urls returned by POST /api/v1/images (upload the files first). For stage=PRELAUNCH/BETA url is optional — the page offers a waitlist. PATCH /api/v1/startups/{slug} Update your startup (e.g. add store links after the app ships). Send only the fields you want to change; an empty string clears a link. Moderation status is not changed. | Field | Type | Req. | Description | |--------------|----------|------|----------------------------------------| | url | string | no | Product website URL | | tagline | string | no | Tagline, up to 160 characters | | description | string | no | Full description, min 50 characters | | categorySlug | string | no | Category slug | | tags | string[] | no | Tags, up to 6 | | targetLaunchDate / prelaunchAudience / prelaunchReady / prelaunchPromise — same as POST | | appStoreUrl, testflightUrl, googlePlayUrl | string | no | App store links (iOS/Android) | result: {slug, url, status, updated: [changed fields]}. The stage is not changed via PATCH — use /stage and /launch (below). Example — add App Store and RuStore links: curl -X PATCH "https://betafinds.com/api/v1/startups/my-app" -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{"appStoreUrl":"https://apps.apple.com/app/id000"}' POST /api/v1/startups/{slug}/stage Change the product stage (except LAUNCHED — use /launch for it). | Parameter | Type | Req. | Description | |-------------|--------|------|------------------------------------------| | stage | string | yes | PRELAUNCH / BETA / PAUSED | | public_note | string | * | Public note (required for PAUSED) | Allowed transitions: PRELAUNCH→BETA/PAUSED, BETA→PAUSED, PAUSED→PRELAUNCH/BETA. result: {slug, stage}. POST /api/v1/startups/{slug}/launch Announce the official launch: the stage becomes LAUNCHED, a LAUNCH update is published, and the waitlist is notified (first launch only). At least one working product link is required. | Parameter | Type | Req. | Description | |-----------------|---------|------|-------------------------------------| | url | string | * | Product link (if not set yet) | | title | string | yes | Launch headline, 3–200 characters | | body | string | yes | Version description, Markdown | | notify_waitlist | boolean | no | Notify the waitlist (default true) | result: {slug, stage, firstLaunch}. GET /api/v1/startups/{slug}/waitlist Waitlist summary for your startup: aggregated counters (active, newPerWeek, newPerMonth, wantsBeta, invited, accepted), the active question with answer distribution, recent anonymized answers and campaigns with aggregated statistics. Participants' contact details are NEVER returned in any form. GET /api/v1/startups/{slug}/reviews?page=1 Public Google Play / App Store reviews of your startup: monitoring state per store (status, lastSuccessAt, publicRating) and reviews, 20 per page (rating, title, body, authorName, territory, reviewCreatedAt, developerResponse). GET /api/v1/startups/{slug}/updates Public list of a startup's updates (no token required). POST /api/v1/startups/{slug}/updates Publish an update. | Parameter | Type | Req. | Description | |-----------|--------|------|--------------------------------------------| | title | string | yes | Title, 3–200 characters | | body | string | yes | Text in Markdown, 10–20000 characters | | type | string | no | RELEASE / UPDATE / FIX / NEWS (default) | | version | string | no | Version, e.g. "1.4.0" | GET /api/v1/startups/{slug}/releases Public list of releases with files and download links (no token required). POST /api/v1/startups/{slug}/releases Create a release. | Parameter | Type | Req. | Description | |-----------|---------|------|--------------------------------------------------| | version | string | yes | Version, unique within the startup | | notes | string | no | Release notes in Markdown | | announce | boolean | no | Whether to publish an update in the feed (default true)| result: {id, version, ...} — id is needed to upload files. POST /api/v1/releases/{release_id}/assets Request a file upload link. | Parameter | Type | Req. | Description | |-----------|--------|------|-------------------------------------------| | platform | string | yes | WINDOWS / MACOS / ANDROID / LINUX | | filename | string | yes | File name, e.g. "MyApp-1.4.0.dmg" | | size | number | yes | Exact file size in bytes | | sha256 | string | no | File SHA-256 (hex) | result: {assetId, uploadUrl, downloadUrl} Maximum file size: 1 GB. uploadUrl is valid for 1 hour. Then upload the file with an HTTP PUT request (body = file): curl -T MyApp-1.4.0.dmg "UPLOAD_URL" POST /api/v1/assets/{asset_id}/complete Confirm the upload. The server verifies the file's presence and size in storage. After that the file appears on the startup page and becomes available at downloadUrl (https://downloads.betafinds.com/...). EXAMPLE: FULL RELEASE CYCLE (bash) ---------------------------------- TOKEN="blt_YOUR_TOKEN" BASE="https://betafinds.com/api/v1" AUTH="Authorization: Bearer $TOKEN" # 1. Create a release with an announcement RELEASE_ID=$(curl -s -X POST "$BASE/startups/my-app/releases" \ -H "$AUTH" -H "Content-Type: application/json" \ -d '{"version":"1.4.0","notes":"- Dark theme\n- Fixed crash on startup"}' \ | jq -r '.result.id') # 2. Request a file upload FILE="MyApp-1.4.0.dmg" SIZE=$(stat -f%z "$FILE" 2>/dev/null || stat -c%s "$FILE") RESP=$(curl -s -X POST "$BASE/releases/$RELEASE_ID/assets" \ -H "$AUTH" -H "Content-Type: application/json" \ -d "{\"platform\":\"MACOS\",\"filename\":\"$FILE\",\"size\":$SIZE}") UPLOAD_URL=$(echo "$RESP" | jq -r '.result.uploadUrl') ASSET_ID=$(echo "$RESP" | jq -r '.result.assetId') # 3. Upload the file directly to storage curl -T "$FILE" "$UPLOAD_URL" # 4. Confirm curl -s -X POST "$BASE/assets/$ASSET_ID/complete" -H "$AUTH" EXAMPLE: PUBLISH NEWS (python) ------------------------------ import requests resp = requests.post( "https://betafinds.com/api/v1/startups/my-app/updates", headers={"Authorization": "Bearer blt_YOUR_TOKEN"}, json={ "type": "FIX", "title": "Fixed sync", "body": "Fixed a rare data loss during offline sync.", }, ) print(resp.json()) LIMITS AND RULES ---------------- - Publishing is only for your own startups with APPROVED status. - A release version is unique within the startup (409 on a duplicate). - Be moderate: the updates feed is visible to all BetaFinds visitors, spam leads to removal of updates and a ban. - Errors: 401 (missing/invalid token), 403 (not your startup), 404 (not found), 409 (conflict), 422 (invalid parameters). Questions: admin@betafinds.com