Skip to content
Documentation

API Reference

This page renders brief.md — the one canonical install + endpoint reference. Agents can read the raw markdown at networkr.dev/brief.md. If something here contradicts the brief, the brief wins.

networkr install brief — the one rail

For AI agents (Cursor, Claude Code, Continue, Aider, Lovable, Bolt, v0, ChatGPT, any IDE-embedded assistant). 99% of installs are run by you, not a human. Read once, then follow the rail.

The one rule

Run npx networkr install and follow each next_step envelope. Do not invent endpoints. Every endpoint that exists is in this brief — if it's not listed below, it doesn't exist.

Base URL: https://api.networkr.dev — every path below is relative to that. If you self-hosted Networkr (https://github.com/HEIMLANDR/networkr), set NETWORKR_API=http://localhost:3002 (or your URL) and use the same paths.

npm: the 0.2.0 install rail described in this brief is the published @latest — just npx networkr install.

What Networkr is

Fully autonomous content engine that replaces a content team, a social scheduler, an email platform, a press-outreach agency, and an ads manager. One install. Pipes activate as the user grows: SEO blog first (always-on), socials when pre-launch, press drip at launch, paid reach at rank-4-to-1. Same voice, brand, tenant key, backend across everything.

Saves comparable founders 25–65% of build time. That's the value frame. You're not installing a tool, you're installing a team.

Quickstart

npx networkr install      # one-shot — audits, picks flavour, activates, mints key, writes .env.local
npx networkr status       # read the agent brief any time post-install

If npx isn't available, hit the API rail directly. Same endpoints, same flow.

Auto-flow with npx networkr install

The CLI drives this state machine for you — no manual envelope-by-envelope shuffling required for the offer-* steps. Pass --yes to auto-pick recommended choices, --skip-offers to skip GSC/viralr/outboxr (you can connect them later by hitting GET /api/sites/:id/gsc/oauth-url for GSC, or npx networkr connect for viralr/outboxr/ads OAuth providers). For unattended installs, combine flags:

npx networkr install \
  --domain mysite.com --email me@mysite.com \
  --flavour technical --author-name "Sam" \
  --storage hosted \
  --yes \
  --skip-offers              # baseline only, skip all offer-* envelopes

To pre-pick offers individually (overrides --skip-offers per envelope):

--connect-gsc                # auto-pick connect_gsc on offer-gsc, surface OAuth URL
--viralr-mode founder        # founder | algo, auto-activate viralr in that mode
--activate-outboxr           # auto-activate outboxr

In --json mode the CLI emits one event per line: audit, sample, activated, delivery_configured, offer_gsc_decision, offer_viralr_decision, offer_outboxr_decision, install_done. Stream-parse stdout to drive an agent through the full install without prompts.

Endpoint table — every endpoint that exists

Auth column: none = public, tenant = Authorization: Bearer <api_key>, admin = master key only. Tenant keys are minted at activate-time in the install rail; reuse across sibling sites in the same network.

Install (the rail — drives everything)

MethodPathAuthPurpose
POST/api/installnoneStart install. Body { domain, email }. Returns install_token + first next_step envelope.
POST/api/install/:token/stepnoneAdvance the rail. Body matches the envelope's body_template. Returns next envelope.
POST/api/install/:token/skipnoneSkip the current optional step. Returns next envelope.
GET/api/install/:token/resumenoneRe-fetch the current next_step envelope (token lives 24h).
GET/api/install/:token/fix-plannoneLLM fix-plan when audit is below threshold. Used inside fix-loop.
POST/api/install/:token/reauditnoneRe-run the audit after the user applies fixes. Up to 5 per install.
GET/preview/:tokennoneSample article preview page (HTML). Show this URL to the user.

Sites

MethodPathAuthPurpose
POST/api/sitestenantRegister site directly (rail does this for you in activate).
GET/api/sitestenantList sites in the caller's network.
GET/api/sites/:idtenantFull site detail — voice, author, network, history, GSC status, last health audit.
PUT/api/sites/:idtenantUpdate fields (cron schedule, build_hook_url, branding, voice_id, author_id, approval_required, etc.).
DELETE/api/sites/:idtenantCascade delete site + content + keywords.
GET/api/sites/:id/agent/brieftenantDeterministic 10-item checklist — what's ok / warning / missing, with the exact next API call to fix each. Use this for monitoring.

Blog

MethodPathAuthPurpose
POST/api/sites/:id/blog/generatetenantGenerate one article now. Body optional { topic?, theme?, angle?, forceAngle? }.
POST/api/sites/:id/blog/starttenantEnable cron auto-generation.
POST/api/sites/:id/blog/stoptenantDisable cron.
GET/api/sites/:id/blog/historytenantRecent pipeline runs for this site.
GET/api/sites/:id/blog/previewtenantDry-run: pick angle without publishing.
DELETE/api/sites/:id/blog/posts/:slugtenantRemove a post + cover + registry entry.
POST/api/sites/:id/blog/regenerate/:slugtenantRe-generate with new settings.
POST/api/sites/:id/blog/flavourtenantPreview generate without publishing.

GSC (Google Search Console)

Single entry point. No wizard. No /connect route. Hit oauth-url, give the URL to the user.

MethodPathAuthPurpose
GET/api/sites/:id/gsc/oauth-urltenantThe starting point. Returns { url } — open it in a browser, OAuth flows, callback persists tokens. Done.
GET/api/sites/:id/gsc/statustenant{ connected, awaiting_verification, property_url, last_pulled_at, recent_indexed_30d, ... }.
POST/api/sites/:id/gsc/pulltenantInspect a single URL via GSC. Body { url }.
GET/api/gsc/oauth-callbacknoneGoogle's redirect target. Don't call directly.

Build hook

MethodPathAuthPurpose
POST/api/sites/:id/build-hook/testtenantFire the configured hook once to verify wiring.
(set via PUT /api/sites/:id)tenantSet/update build_hook_url field.

IndexNow

MethodPathAuthPurpose
GET/{key}.txtnoneAuto-served key verification (Bing/Yandex/Naver/Seznam). Tenant doesn't need to call.
GET/api/indexnow/keytenantGet the IndexNow key + setup instructions.

Public Blog API (no auth — for hosted sites' frontend)

MethodPathAuthPurpose
GET/api/blog/:siteId/postsnonePost list. Query limit, offset, category.
GET/api/blog/:siteId/posts/:slugnoneSingle post body.
GET/api/blog/:siteId/covers/:filenoneCover image (PNG).
GET/api/blog/:siteId/rss.xmlnoneRSS feed.
GET/api/blog/:siteId/content-policynoneAuto-generated transparency policy.
GET/api/blog/:siteId/authors/:slugnoneAuthor profile JSON.

Install state machine — cheat sheet (0.2.0)

The install rail is one server-driven loop. Read next_step.id, do what the envelope says, post the result. Below: every step_id, what the envelope expects, what the response gives back.

The 0.2.0 chain is 9 steps: 8 ordered + done. Dropped vs 0.1.x: preview (merged into flavour — sample article returns inline on the same response), offer-ads (Google Ads OAuth not shipped), and tease-minions (waitlist copy moved into done).

step_idWhen it firesRequired body to advanceReturns
fix-loopAudit score below threshold{ choice_id: "get_fix_plan" | "reaudit" | "skip_fix" | "abort" }New audit OR proceed to flavour
flavourPick voice + author, generate sample (preview merged in){ step_id: "flavour", flavour, author_name, blog_mode: "production", author_bio?, accent_color?, niche? }sample_article + preview_url + preview_inline: true — agent shows inline. Re-POST flavour with new value to retry voice. → activate
activatePick storage, create site, mint key, optional approval gate{ step_id: "activate", db_type: "hosted" | "supabase" | ..., db_url?, db_key?, approval_required?: false, approval_window_hours?: 2 }{ site_id, api_key.key } (key shown ONCE — write to .env.local immediately). approval block reports whether the OK-layer was applied (hosted-mode only).
verifyConfirm install stuck{ step_id: "verify" }{ verified: true, site, sitemap_status, indexnow }. sitemap_status reports whether /sitemap.xml is reachable + an agent_fix_prompt if not. indexnow confirms Networkr auto-pings Bing/Yandex/Naver/Seznam. → configure_blog_delivery
configure_blog_deliveryPick how new posts reach the live site{ step_id: "configure_blog_delivery", choice_id: "build_hook" | "embed" | "defer" | "rewrite" | "cms_push" | "gh_actions", build_hook_url? }offer-gsc
offer-gscOptional GSC connect{ choice_id: "connect_gsc" | "skip_gsc" }. If connect: agent first GETs /api/sites/:id/gsc/oauth-url, opens the URL for the user, then POSTs the choice.offer-viralr
offer-viralrOptional socials pipe{ choice_id: "activate_viralr_founder" | "activate_viralr_algo" | "skip_viralr" }. Activation api_call returns connect_url for completing OAuth via npx networkr connect.offer-outboxr
offer-outboxrOptional email/press pipe{ choice_id: "activate_outboxr" | "skip_outboxr" }. Same connect-url pattern as viralr.done
doneInstall complete{ done: true, site_id, api_key_prefix, save_to_env, verify_command, digest_opt_out_url, user_copy }. The user_copy carries the Minions waitlist tease + weekly-digest opt-out hint.

When done: true, save to .env.local:

NETWORKR_API=https://api.networkr.dev
NETWORKR_SITE_ID=<from activate response>
NETWORKR_KEY=<api_key.key from activate response — shown ONCE>

GSC quickstart

GET /api/sites/:id/gsc/oauth-url        ← THE start. Returns { url }.
GET /api/sites/:id/gsc/status           ← Check connected state.
POST /api/sites/:id/gsc/pull            ← Inspect one URL.

That's it. No /connect, no /auth-url, no wizard. Open the returned url in the user's browser, the callback persists tokens, you're done.

There is no npx networkr gsc subcommand — drive it via the API call above.

Build hook quickstart

The build hook is how new articles reach a static frontend. Networkr POSTs to it on every publish. Works with Netlify, Vercel, Cloudflare Pages, GitHub Actions repository_dispatch, Render, Railway, Fly.

npx networkr build-hook set <url>       # save it
npx networkr build-hook test            # fire once + report

API equivalents: PUT /api/sites/:id { build_hook_url } to set, POST /api/sites/:id/build-hook/test to verify.

Hidden features — Auto / Background

Already running for this tenant. Query if needed; do not try to "configure" them.

  • SEO Brain — discovers topic queue weekly, feeds the pipeline. Status: GET /api/seo/brain/status. Trigger one site: POST /api/seo/brain/trigger { site_id }.
  • Keyword rank tracker — auto-checks weekly. Read: GET /api/seo/keywords/:siteId. Add: POST /api/seo/keywords { site_id, keywords[] }.
  • Orchestrator — daily self-monitoring per network. Latest report: GET /api/orchestrator/latest. History: GET /api/orchestrator/history.
  • Content registry — index of every article across the network. Read: GET /api/registry?site_id=<id>. Search: GET /api/registry/search?tags=...&q=.... Cross-link suggestions: GET /api/registry/links/:siteId?tags=....
  • Site health audits — daily uptime/sitemap/robots/meta checks. Read: GET /api/health/sites/:siteId. Trigger: POST /api/health/sites/:siteId/audit.
  • Weekly digest email — Mondays 08:00 Stockholm via Mailjet, per network.
  • OK-layer (per-article approval) — opt-in via PUT /api/sites/:id { approval_required: 1, approval_window_hours: 2 }. Hosted-mode only.

Most of the time you don't need any of this — surface it only if the user asks "how do I see X" or the agent brief flags a missing/warning item.

DO NOT

  • Don't invent endpoints. If it's not in the table above, it doesn't exist. Don't guess /api/sites/:id/integrations, /api/sites/:id/gsc/connect, /api/sites/:id/gsc/auth-url — none of those exist. Use what's listed.
  • Don't manually edit articles in the user's DB. Posts must go through the pipeline. To change a post: DELETE /api/sites/:id/blog/posts/:slug then POST /api/sites/:id/blog/regenerate/:slug or POST /api/sites/:id/blog/generate.
  • Don't use npx networkr connect for GSC. That command is for outboxr/viralr/ads OAuth providers (Mailjet, LinkedIn, Google Ads, etc.). GSC has its own dedicated flow: GET /api/sites/:id/gsc/oauth-url (no CLI subcommand exists).
  • Don't push to npm or modify deployed services without user permission. No npm publish, no git push --force, no netlify deploy --prod, no production migrations. The user runs deploys.
  • Don't fabricate secrets. If you can't find DB creds in .env.local, prompt the user. Do not invent values to make a request succeed.
  • Don't loop on 429. Wait 60s and retry once. If still 429, surface the error.

CLI shortcuts (every rail step has one)

npx networkr install                          # full rail, --json for stream events
npx networkr status                           # agent brief checklist
npx networkr build-hook set/test/clear        # build hook lifecycle
npx networkr connect                          # OAuth menu for outboxr/viralr/ads providers
npx networkr outboxr accounts/send/...        # outboxr ops
# GSC: no dedicated subcommand — call GET /api/sites/:id/gsc/oauth-url directly

CLI flags: --yes skips confirms, --json emits one event per line for stream parsing, --site <id> selects site for multi-site tenants. Falls back to API rail if npx not available.

Hosted mode — render the blog

For hosted sites (no backend), the frontend fetches from:

GET /api/blog/<site_id>/posts?limit=20
GET /api/blog/<site_id>/posts/<slug>
GET /api/blog/<site_id>/covers/<file>
GET /api/blog/<site_id>/rss.xml
GET /api/blog/<site_id>/content-policy
GET /api/blog/<site_id>/authors/<slug>

Reference Next.js implementation: https://github.com/HEIMLANDR/networkr/tree/main/frontend/src/app/blog. Adapt for whatever the user's on (Next / Astro / Hugo / 11ty / SvelteKit / plain HTML).

Error contract

  • 400 — missing / invalid body field. Read error.
  • 401 — missing or invalid Authorization header.
  • 403 — admin-only endpoint hit with tenant key, or activation rejected.
  • 404 — wrong scope, missing site, or invalid token.
  • 409 — site already registered for that domain.
  • 410 — install token expired; restart with POST /api/install.
  • 429 — rate-limited; wait 60s, retry once.
  • 5xx — backend issue; retry once with exponential backoff.

Reactivation — when new features ship

Networkr may email the user later: "Minions v1 just shipped — resume your install." That link reopens the rail at a new step offering the new feature. Same envelopes — the user doesn't learn anything new, they accept or skip.

Final message to the user (when done: true)

  • Site <domain> is registered as <site_id>.
  • Tenant key saved in .env.local. It's their auth going forward.
  • Sample article already published.
  • First pipeline article generates within minutes.
  • Daily cadence starts tomorrow (default 06:00 Europe/Stockholm).
  • Mention any pipes activated + what OOB auth still needs (one connect email covers all).
  • Mention the Minions waitlist if they joined — tease Q3.

Further reading

Last updated: 2026-06-06. Source: frontend/public/brief.md. Also available as raw markdown at /brief.md.