Skip to main content
Quickstart

Submit a SaaS Listing From the Terminal

Install the SubmitForBacklinks CLI, scan a public product URL, create a draft, explicitly submit it, and retrieve its review status.

1. Install the single-file CLI

The CLI requires a current Node.js runtime. This fail-closed installer pins and verifies the release SHA-256 with Node before making it executable. Keep it in the working directory or move it into a user-owned bin directory.

macOS or Linux
set -eu
SFB_CLI_FILE='submitforbacklinks-1.1.0.mjs'
SFB_CLI_SHA256='669e7894aa7c1914d9b2e432aa05a0be87c33dc2b68edbcc6601880e385526ee'
curl -fsSL 'https://submitforbacklinks.com/downloads/submitforbacklinks/1.1.0' -o "$SFB_CLI_FILE"
node - "$SFB_CLI_FILE" "$SFB_CLI_SHA256" <<'SFB_VERIFY'
const { createHash } = require("node:crypto");
const { readFileSync } = require("node:fs");
const [file, expected] = process.argv.slice(2);
const actual = createHash("sha256").update(readFileSync(file)).digest("hex");
if (!/^[a-f0-9]{64}$/.test(expected) || actual !== expected) {
  console.error("Checksum verification failed.");
  process.exit(1);
}
console.log(file + ": checksum verified");
SFB_VERIFY
chmod +x "$SFB_CLI_FILE"
ln -sf "$SFB_CLI_FILE" submitforbacklinks
./submitforbacklinks --version

2. Verify the product domain in the terminal

Setup asks for a contact email for submission notices, not an email code. Publish the exact short-lived .well-known challenge on the product domain. The CLI then stores its credential in a user-owned config file with 0600 permissions. There is no Settings visit, copied key, password, or .env file.

Set up terminal ownership
./submitforbacklinks setup https://yourproduct.com \
  --email [email protected]
# Add and deploy the exact .well-known file returned above, then:
./submitforbacklinks setup --verify

How an agent should help

If the agent can edit and deploy the product repository, it should add the returned proof file, deploy it, run verification, and offer to remove the proof afterward. Otherwise it should ask the owner only to publish that exact file.

3. Scan and review the product site

Scanning is stateless: it does not create a listing, reserve the domain, or guarantee that draft creation will succeed. Save its output, then inspect and correct the proposed content while keeping source URLs, warnings, and confidence available as review context.

Save a proposed draft for review
./submitforbacklinks scan https://yourproduct.com --pretty > scan.json

4. Save a draft

Pass the reviewed scan file so the saved draft uses the content you inspected instead of starting a separate unseen scan. Generate and store a new UUID for this distinct mutation. Reuse that exact key only if the identical request must be retried. A draft is owner-scoped and does not enter editorial review.

Create an owner-scoped draft
# Inspect and edit scan.json before creating anything.
export SFB_DRAFT_KEY="$(node -e 'console.log(crypto.randomUUID())')"
./submitforbacklinks draft https://yourproduct.com \
  --from-scan scan.json \
  --idempotency-key "$SFB_DRAFT_KEY"

5. Review the saved state and correct it if needed

Retrieve the submission ID returned by draft, save the exact server state, and present its submission fields to the owner. If any field needs correction, edit reviewed-status.json, generate and store a new UUID for the update, update the draft, and retrieve it again. Update sends the complete nine-field listing set rather than a partial patch. A --from-status file is bound to its submission ID. You may instead use --from-scan, but the two options are mutually exclusive. Every CLI mutation using --from-scan validates its source hostname against the submitted website; update performs an owner-scoped status preflight and therefore needs submissions:read as well as submissions:write.

Save the exact draft state for review
./submitforbacklinks status 00000000-0000-4000-8000-000000000000 --pretty > reviewed-status.json

Replace the reviewed saved fields
export SFB_UPDATE_KEY="$(node -e 'console.log(crypto.randomUUID())')"
./submitforbacklinks update 00000000-0000-4000-8000-000000000000 \
  --from-status reviewed-status.json \
  --idempotency-key "$SFB_UPDATE_KEY"

6. Finalize only after explicit review

Generate and store a new UUID for finalization. All four confirmation flags are required; the CLI will not silently infer them. Add --week YYYY-MM-DD to request an available launch week, or omit it to use the next available week.

Send the saved draft for editorial review
export SFB_FINALIZE_KEY="$(node -e 'console.log(crypto.randomUUID())')"
./submitforbacklinks finalize 00000000-0000-4000-8000-000000000000 \
  --idempotency-key "$SFB_FINALIZE_KEY" \
  --confirm-terms \
  --confirm-badge \
  --confirm-representative \
  --confirm-reviewed

What the four flags mean

You accept the terms and guidelines, understand the free badge requirement, are authorized to represent the product, and have reviewed the generated listing content for accuracy.

7. Track editorial status

Use the submission ID returned by draft or submit. Detail responses are restricted to the API-key owner.

Check one submission
./submitforbacklinks status 00000000-0000-4000-8000-000000000000

Optional: edit from the dashboard too

Claiming is not required for terminal submission or editing. The command returns a 15-minute, one-use link. Open it and sign in with Google or an existing account; the terminal credential keeps working.

Create a dashboard handoff
# Optional: connect the terminal-owned submissions to the web dashboard.
./submitforbacklinks claim

Optional media stays in the browser dashboard

A logo and up to three screenshots are optional. Agent API v1 and the CLI do not upload or replace media.

The owner can add or manage media from the signed-in browser dashboard.

REST-only alternative

The CLI is optional. This minimal request creates a server-scanned draft. Retrieve and review the actual saved draft before correcting or finalizing it. Use the same bearer token and a caller-generated UUID as the idempotency key.

Create a draft with curl
export SFB_CURL_DRAFT_KEY="$(node -e 'console.log(crypto.randomUUID())')"
curl --fail-with-body --request POST \
  https://submitforbacklinks.com/api/v1/agent/submissions \
  --header "Authorization: Bearer ${SUBMITFORBACKLINKS_API_KEY}" \
  --header "Idempotency-Key: ${SFB_CURL_DRAFT_KEY}" \
  --header "Content-Type: application/json" \
  --data '{
    "websiteUrl": "https://yourproduct.com",
    "action": "draft",
    "listingType": "FREE"
  }'