Skip to content
Metric VaultHelp Center
Open app

All endpoints

The complete Metric Vault route table, every path the Worker answers, with method, authentication mechanism and purpose.

Last updated 2026-08-06

Summary#

This is the complete list of HTTP routes served by the Metric Vault Worker, grouped by feature area. Only POST /api/v1/analyze and the three /api/keys/* management routes are a supported public integration surface; everything else exists to serve the web app, the Chrome extension, the public blog and the operator tooling. They are documented here so a developer or a support engineer can tell exactly what a request did and what guarded it.

Warning

Warning: Routes outside /api/v1/ and /api/keys/ are internal. Their request and response shapes change with the product and carry no compatibility promise. Build against POST /api/v1/analyze.

Overview#

How routing works#

Every request to metricvaultai.com reaches one Cloudflare Pages Worker before anything else. The Worker evaluates a flat, ordered chain of path checks and the first match wins; anything unmatched falls through to the static assets. Order is load-bearing in a few places, most notably that the Stripe webhook is matched before any body-reading middleware so the raw bytes stay available for signature verification, and that /api/blog/site and /api/blog/sites are matched before the /api/blog/* catch-all.

Fifteen paths never reach the Worker at all: /free/*, /blog.html, /favicon.ico, /favicon.png, /favicon-16x16.png, /favicon-32x32.png, /apple-touch-icon.png, /og-image.png, /logo-dark.png, /logo-light.png, /icon-192.png, /icon-192.svg, /icon-512.png, /icon-512.svg and /manifest.json. They are served directly as static assets.

Authentication mechanisms#

Seven distinct mechanisms appear in the tables below.

Note

Verified session is the one to read carefully. MV_C1_ENFORCE is not set in production, so a route marked that way rejects a caller who sends no address at all, but still believes the address it is given. Ownership therefore rests on a claim until enforcement is switched on. See Authorization model.

Billing does not wait for the flag. Any route that spends credits requires a proven session whatever MV_C1_ENFORCE says. An unverified caller gets 401 unauthorized; a caller whose session could not be checked because the identity provider was unreachable gets 503 auth_unavailable and should retry rather than sign in again. /api/analyst is the exception and answers anonymous callers, because it also serves the public chat — it bills them to nobody instead. Routes that cost nothing, /api/chatbot among them, stay open and are capped per IP.

LabelMechanismNotes
Session emailThe handler reads user_email from the JSON body, the x-mv-user header, or a query parameter, and uses it as the actorNot a verified credential. Used by the majority of app endpoints
Verified sessionmvC1Identity prefers a verified bearer token and falls back to the address in the body. The route refuses a caller it cannot name at allUnder MV_C1_ENFORCE=1 an unverified caller is refused outright; that flag is off in production today
Verified session, owner of the rowThe same, plus the stored row’s user_email must match the callerA row with no owner was created by the signed-out free tool and stays reachable by its id, because the id is the only handle its creator ever had
Verified tokenA Metric Vault session access token is verified against the identity provider, and the address must have a confirmed emailUsed by the key-management routes and saved-result deletion
Admin tokenA session token that also resolves to an active row in the administrator tableAll /api/admin/* and the workflow-failure console
Internal secretHeader x-mv-internal-secret, or ?key= on a GET, matched against MV_INTERNAL_SECRET. Fails closed when unsetOperator and machine routes. See Internal endpoints
API keyAuthorization: Bearer mv_live_.../api/v1/analyze only
PublicNo credentialGenuinely open, or protected by something else such as a signature
Note

Where the truth lives. _worker.js is the only authority; this table is a hand-maintained copy of it and has drifted before. tests/api-auth-doc.mjs compares the two on every deploy and fails if a row claims a gate the handler does not implement.

It is not GENERATED from the source, and that is deliberate rather than unfinished. Three things defeat generation today. A classifier has to know every identity helper by name — there are ten, and one it does not know turns a correctly documented route into a wrong one, which is how an earlier version of the checker reported /api/blog/sites and /api/library/saved/delete as unprotected when both are gated. Several routes are guarded in the router rather than the handler — /api/dbgdfs compares ?key= against MV_INTERNAL_SECRET before any handler is reached — and a check that reads handlers cannot see them. And a prefix route is not one gate: /api/blog/<rest> is a sub-router whose branches differ from each other, so no single generated label is true of it. The checker is where the derivation belongs; the table stays readable prose that the checker holds to account.

CORS#

Successful JSON responses carry Access-Control-Allow-Origin: *. The OPTIONS preflight answers for every path with:

text
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Note

Note: The preflight advertises only GET, POST, OPTIONS and only the Content-Type and Authorization headers. The blog sub-router uses PATCH, PUT and DELETE and the custom headers x-mv-user and x-mv-site, so cross-origin blog writes fail preflight. Same-origin calls from the dashboard are unaffected.

The Chrome-extension endpoint uses a wider set that also allows the X-Extension-Version header.


Billing and account#

PathMethodAuthPurpose
/api/create-checkoutPOSTSession emailCreates a Stripe Checkout session for a plan and billing period and returns its URL
/api/stripe/webhookPOSTStripe signatureReceives subscription lifecycle events and grants or revokes plans
/api/billing/syncPOSTInternal secretReconciles stored plans against live Stripe subscriptions
/api/manage-subscriptionPOSTVerified sessionReturns a Stripe Customer Portal session URL
/api/usagePOSTVerified sessionPlan, month-to-date credits, quota, reset date and per-tool breakdown

Library, saved results and history#

PathMethodAuthPurpose
/api/library/savedGETVerified sessionLists saved tool results, newest first
/api/library/saved/checkPOSTVerified sessionReports whether a saved result already matches these tool inputs
/api/library/saved/openGETVerified sessionReturns one saved result, ownership enforced in the query
/api/library/saved/htmlPOSTVerified session, owner of the rowStores the rendered HTML of a saved result
/api/library/saved/deletePOSTVerified tokenDeletes saved results. Deliberately ignores any body email
/api/library/save-runPOSTVerified sessionSaves a completed run submitted by the client
/api/historyPOSTVerified sessionOlder analysis history read from the identity provider's database
/api/save-analysisPOSTVerified sessionWrites an analysis row to that same database

Tools and AI#

PathMethodAuthPurpose
/api/toolsPOSTSession email, optionalRuns one of the 18 direct-fetch technical tools. Anonymous callers are rate-limited by IP
/api/premium-aiPOSTSession email, optionalThe main analysis endpoint behind most dashboard tools. Applies the shared data cache, credit metering and result saving
/api/analystPOSTVerified sessionConversational analyst behind the in-app assistant and the marketing widget; metering and charges land only on a verified caller, never on a claimed address
/api/briefPOSTSession email, optionalThe daily briefing shown on dashboard load. A signed-in caller gets a brief built from their own monitored competitors, cached one per account per UTC day; an anonymous caller is answered a generic brief and rate-limited by IP. Not charged
/api/opportunitiesPOSTVerified sessionThe Opportunities card shown on dashboard load. Returns five prioritized opportunities for a target. Answers 401 auth_required to a caller it cannot name; not charged, cached one per account per target per ISO week
/api/originalityPOSTVerified sessionDuplicate-content and AI-likelihood check on pasted text
/api/factcheckPOSTVerified sessionExtracts claims from pasted text and verifies them with citations
/api/chatbotPOSTPublic, IP rate-limitedThe support assistant behind the chat bubble on the public site. Answers signed-out visitors, capped per IP address; a signed-in caller is metered on the fair-use bucket instead. Forwards up to ten truncated user/assistant turns to gpt-4o. Its plan, price and billing facts are built from MV_PUBLIC_PLANS and the plan tables, and never charged
/api/translatePOSTPublic, IP rate-limitedTranslates dynamic result strings, cached permanently per string
/api/cited-sourcesPOSTVerified sessionAggregates the sources cited about a brand across research queries
/api/benchmarksPOSTVerified sessionIndustry benchmark comparison for a domain, cached 30 days
/api/extension/quickviewPOSTPublic, IP rate-limitedThe Chrome extension's domain snapshot, cached 24 hours. Answers an anonymous caller and charges nothing, because the extension has no sign-in. A verified bearer token is metered at 1 credit and skips the IP cap; an address sent in the body without one is ignored rather than believed

The 18 tool types accepted by /api/tools are onpage_seo, pagespeed, robots_audit, schema_check, http_headers, sitemap_validator, redirect_chain, mixed_content, hreflang_checker, tech_stack, dup_content, internal_linking, ai_visibility, geo_audit, ai_overview, ai_citation, seo_compare and llms_txt. Anything else returns Invalid tool type: <type>.

Article writer#

The Article Writer runs through POST /api/premium-ai like every other dashboard tool, with the tool type in the request body.

There used to be a separate three-route job queue here — /api/article-start, /api/article-status and /api/article-process. It was removed on 2026-09-01: nothing in the product called it, and /api/article-start was public. See Internal endpoints for why an internal handoff cannot carry a caller's identity.

Tier-2 AI visibility#

PathMethodAuthPurpose
/api/tier2/prompts/listPOSTSession emailLists tracked prompts for a brand
/api/tier2/prompts/addPOSTSession emailAdds a tracked prompt
/api/tier2/prompts/deletePOSTSession emailRemoves a tracked prompt
/api/tier2/alerts/listPOSTVerified sessionLists visibility-drop alerts
/api/tier2/alerts/addPOSTVerified sessionCreates a drop alert, threshold 1 to 99 percent
/api/tier2/alerts/deletePOSTVerified session, owner of the rowDeletes a drop alert
/api/tier2/trends/exportPOSTVerified sessionExports a brand's visibility trend as JSON or CSV, 1 to 365 days
/api/tier2/comparePOSTVerified sessionCompares up to 5 brands over 1 to 180 days

Competitor Monitor#

All ten are POST, authenticated by session email, and cost no credits.

PathPurpose
/api/monitor/listLists monitored URLs
/api/monitor/addAdds a URL to monitor and runs the first check immediately
/api/monitor/deleteRemoves a monitored URL
/api/monitor/updateUpdates label, interval, sensitivity, severity gate or alert channels
/api/monitor/changesLists detected changes, 1 to 500 per page
/api/monitor/snapshotReturns stored snapshots for one monitored URL
/api/monitor/check-nowForces an immediate check
/api/monitor/mark-readMarks changes read or dismissed. id may be all
/api/monitor/statsAggregate counts, timeline and most volatile domains
/api/monitor/test-alertSends a synthetic High-severity alert to the supplied channels

Responsive Website Analyzer#

PathMethodAuthPurpose
/api/responsive/analyzePOSTSession email, optionalFetches a page and returns a responsiveness analysis
/api/responsive/chatPOSTVerified session, owner of the rowFollow-up questions about a stored analysis
/api/responsive/historyPOSTVerified sessionPrevious analyses
/api/responsive/getPOSTVerified session, owner of the rowReturns one stored analysis
/api/responsive/deletePOSTVerified session, owner of the rowDeletes one stored analysis
/api/responsive/devicePOSTVerified session, owner of the rowReturns the analysis for one device profile
/api/responsive/proxyGETPublicRenders a target page inside the preview frame. Returns a styled HTML card on failure, not JSON
/api/responsive/assetGETPublicProxies a sub-resource for the preview frame and rewrites CSS URLs to absolute

Google and social integrations#

PathMethodAuthPurpose
/auth/google/startGETPublic, ?email=Begins the Google OAuth flow with a server-stored state token
/auth/google/callbackGETServer-stored stateCompletes Google OAuth and stores the tokens
/api/google/statusPOSTSession emailWhether Google is connected and for which address
/api/google/disconnectPOSTSession emailRemoves the stored Google tokens
/api/gsc/sitesPOSTVerified sessionLists Search Console properties available to the account
/api/gsc/performancePOSTSession emailSearch Console performance rows, cached 1 hour
/api/social/connect-urlPOSTVerified sessionMints a single-use ticket for a social OAuth flow, tied to the signed-in account
/api/social/connectGETSession email (one-time ?t= ticket)Begins a social OAuth flow from a ticket minted by /api/social/connect-url. Errors render as HTML, not JSON
/api/social/callback/<platform>GETServer-stored stateCompletes a social OAuth flow. State is single-use and expires after 15 minutes
/api/social/connectionsGET, POSTVerified sessionLists connected social accounts
/api/social/disconnectPOSTVerified sessionRemoves one social connection
/api/social/publishPOSTVerified sessionPublishes text to the selected channels
/api/social/schedulePOSTVerified sessionQueues a post for a future time
/api/social/scheduledGET, POSTVerified sessionLists queued posts, pending first
/api/social/scheduled/cancelPOSTVerified sessionCancels a pending queued post

The six supported platforms are LinkedIn, X, Facebook, Instagram, TikTok and Threads. Instagram and TikTok return a "needs media" notice on text publishing.

Team and workspaces#

PathMethodAuthPurpose
/api/team/listPOSTSession emailMembers of your workspace and workspaces you have joined
/api/team/invitePOSTSession emailInvites a teammate. Enforces the plan's seat cap
/api/team/acceptPOSTInvite token plus matching emailAccepts an invitation
/api/team/removePOSTSession emailRemoves a member from your workspace
/team/accept/<token>GETPublicSelf-contained HTML page that posts to /api/team/accept

Notifications, preferences and branding#

PathMethodAuthPurpose
/api/notificationsPOSTVerified sessionAggregated feed of competitor changes, rank drops, scheduled runs and share views
/api/notifications/mark-readPOSTVerified sessionMarks competitor-change items read
/api/prefs/notifications/getPOSTVerified sessionReads the four notification preferences
/api/prefs/notifications/savePOSTVerified sessionSaves the four notification preferences
/api/branding/getPOSTVerified sessionReads white-label branding
/api/branding/savePOSTVerified session plus Pro planSaves logo, colors, company name and footer
/api/branding/resetPOSTVerified sessionRestores the default branding

Alerts, schedules, editorial, publishing, sharing#

PathMethodAuthPurpose
/api/rank-alerts/createPOSTVerified sessionCreates a keyword rank alert and takes an immediate baseline
/api/rank-alerts/listPOSTVerified sessionLists rank alerts
/api/rank-alerts/deletePOSTVerified sessionDeletes an alert and its history
/api/rank-alerts/check-nowPOSTVerified session, owner of the rowForces an immediate rank check
/api/schedules/createPOSTVerified session plus Starter planCreates a scheduled report
/api/schedules/listPOSTVerified sessionLists scheduled reports
/api/schedules/deletePOSTVerified sessionDeletes a schedule and its run history
/api/editorial/listPOSTVerified sessionLists editorial calendar items
/api/editorial/createPOSTVerified sessionCreates a calendar item
/api/editorial/updatePOSTVerified session, owner of the rowUpdates title, date, status, owner, notes or order
/api/editorial/deletePOSTVerified sessionDeletes a calendar item
/api/publish/statusGET, POSTVerified sessionReports which CMS platforms this account has connected, and which can publish live at all
/api/publish/pushPOSTVerified sessionPublishes a post. WordPress publishes for real; the other five refuse and say why. Draft unless publish_live: true
/api/publish/connectPOSTVerified sessionVerifies and stores a WordPress site URL, username and Application Password
/api/share/createPOSTVerified sessionCreates a public share link for a rendered report
/api/share/listPOSTVerified sessionLists your share links
/api/share/deletePOSTVerified session, owner of the rowDeletes a share link
/api/workflow/failurePOSTSession emailClient-reported workflow step failure, for triage
/api/workflow/failuresPOSTAdmin tokenLists reported failures with aggregate statistics
/api/workflow/failure/resolvePOSTAdmin tokenMarks a reported failure resolved

Blog#

PathMethodAuthPurpose
/api/blog/siteGET, POSTBlog identityCurrent site payload: ids, public URLs, feed URL and embed snippet
/api/blog/sitesGET, POST, PATCHBlog identity, writes owner-onlyLists, creates, selects and renames blog sites
/api/blog/rolePOSTSession emailSets a team member's blog role
/api/blog/higgsfield/signinGETInternal secretOperator sign-in for the image provider
/api/blog/higgsfield/callbackGETServer-stored PKCE stateCompletes that operator sign-in
/api/blog/media/file/<siteId>/<file>GETPublicStreams a stored media object, cached immutably for a year
/api/blog/public/<siteId>/<rest>GET, POSTPublicThe public blog API: posts, posts/<slug>, feed.rss, subscribe, unsubscribe/<token>
/api/blog/<rest>GET, POST, PATCH, PUT, DELETEBlog identity; role capability on some branches onlyThe authenticated blog sub-router
Note

The blog sub-router's capability checks are uneven. Identity is required for every branch, but mvbCan is not. Thirteen branches carry no capability check at all, including POST posts (create), POST media (upload), POST media/import (a server-side fetch of a caller-supplied URL) and both metered generators, POST ai/blog and POST ai/image — every cheaper ai/* branch beside them opens with a postWriteOwn check. Two further points matter when reading the role model: postWriteOwn and media are true for all three roles, so the guards that use them refuse nobody, and mvbNormalizeRole coerces an unrecognised role to author. Only postWrite, postDelete and taxonomy can actually refuse a caller, and only the author role. See Authorization model.

The authenticated sub-router covers posts, categories, tags, authors, media, stock search, subscribers, newsletter templates and sending, AI generation and AI translation. Blog roles are owner, editor and author; anything else is coerced to author. See Roles and what each can do.

API keys and the public API#

PathMethodAuthPurpose
/api/keys/createPOSTVerified tokenCreates a key and returns it once. Maximum 5 active
/api/keys/listPOSTVerified tokenLists your keys in masked form
/api/keys/revokePOSTVerified tokenRevokes one key by id
/api/v1/analyzePOSTAPI keyThe public analysis endpoint. 6 credits per call

Administration#

All 22 routes are POST and take a session token in the body as token. A caller that is not an active administrator receives HTTP 403 with {"error":"Unauthorized"}.

PathOwner onlyPurpose
/api/admin/meNoReturns the caller's admin email and role
/api/admin/dataNoSubscribers, usage and headline statistics for the month
/api/admin/metricsNoHourly metrics, window capped at 168 hours
/api/admin/errorsNoGrouped error issues
/api/admin/errors/resolveNoResolves or reopens an error group
/api/admin/usersNoLists administrators
/api/admin/users/mutateYesAdds, updates or removes an administrator
/api/admin/auditNoReads the admin audit log
/api/admin/runsNoReads the activity log across accounts
/api/admin/blog/postsNoLists blog posts across accounts
/api/admin/blog/posts/statusYesModerates a post to draft, published or trash
/api/admin/configNoLists platform configuration keys
/api/admin/config/setYesSets or deletes a configuration key
/api/admin/toolsNoPer-tool credit costs, sections and cache lifetimes
/api/admin/jobsNoBackground job status and next-due times
/api/admin/jobs/runYesTriggers one job: monitor, schedules, rank_alerts or tier2
/api/admin/cache/clearYesClears the shared data cache, optionally for one tool
/api/admin/cache/setallYesSets the global cache lifetime, 1 to 365 days
/api/admin/customerNoOne customer's plan, usage, connections and team
/api/admin/customer/mutateYesSets plan, resets usage, refunds credits, suspends, reactivates, removes a seat
/api/admin/customer/billingNoOne customer's plan source and live Stripe subscription
/api/admin/social/disconnectYesRemoves a customer's social connection

Internal and diagnostic routes#

PathMethodAuthPurpose
/api/cron/runPOST, GETInternal secretRuns whichever background jobs are due
/api/billing/syncPOSTInternal secretReconciles plans against Stripe
/api/dbgdfsGETInternal secret via ?key=Raw data-provider probe
/api/diag/psiAnyInternal secret via ?key=Reports which PageSpeed keys are loaded
/api/diag/websearchAnyInternal secret via ?key=Runs one real web-search call
/api/diag/emailAnyInternal secret via ?key=Sends one real test email
/api/diag/aiwriteAnyInternal secret via ?key=Runs one real article-writer model call
/api/diag/stockAnyInternal secret via ?key=Runs a real stock-photo search
/api/diag/blog-imageAnyInternal secret via ?key=Reports image-generation readiness
/api/diag/subsAnyInternal secret via ?key=Per-site newsletter subscriber counts
/api/diag/keywordgapAnyInternal secret via ?key=Raw keyword-gap probe. Makes billable provider calls
/api/diag/socialAnyInternal secret via ?key=Per-platform OAuth configuration and redirect URIs

Full detail is in Internal endpoints and Diagnostic endpoints.

Non-API routes served by the Worker#

PathMethodBehavior
/share/<slug>GETPublic shared report. 404 page when unknown, 410 when expired. Increments the view count
/team/accept/<token>GETInvitation acceptance page
/login, /login.htmlGETReturns an inlined copy of the sign-in page with no-store headers. Never touches the static assets
/blog, /blog.htmlGETServes the blog index, edge-cached 300 seconds
/blog/<slug>GETServes the blog post shell
/legal, /legal.htmlGETServes the legal page, edge-cached 300 seconds
/compare, /compare.htmlAny301 to /pricing
/sitemap.xmlGETInline sitemap, cached 1 hour
/robots.txtGETInline robots file, cached 1 hour
/logo-dark.png, /logo-light.pngGETLogo images, edge-cached 1 hour
/ext-preview.htmlGETExtension preview page, never cached
/free, /free/…GETNot handled. _routes.json excludes /free/* from the Worker, so these paths are served as static assets and return 404. /free-tools/ is the only URL for the hub
/privacy, /terms, /legal/cookies and variants (7 paths)GET301 to the canonical legal pages, cached 1 day
/free-tools, /free-tools/schema-validator, /free-tools/robots-checker, /free-tools/pagespeed-audit and variants (9 paths)GETServes the free-tool page, edge-cached 300 seconds

Anything else falls through to the static assets. Clean URLs are remapped (/ to /index.html, /dashboard to /dashboard.html, and the same for /login, /admin, /privacy and /terms). A 404 on a path with no file extension is upgraded to the branded 404 page; a 404 on a path that has a file extension keeps its bare 404, so a missing script never receives an HTML body.

Paths that are not routes#

PathReality
/api/integration-waitlistCalled by the dashboard's integration waitlist form, but no handler exists. The request falls through and returns 404, and the client swallows it
/api/broadcastBelongs to the vendored identity-provider library, not to Metric Vault
/blog.html, /logo-dark.png, /logo-light.pngHandlers exist in the Worker but these paths are excluded from Worker routing, so the static asset answers first

Request logging#

Every request passes through an activity wrapper before and after routing. It records a row in the activity log for recognized routes, using the actor resolved from the body, the x-mv-user header, or a query parameter. Read-only polling routes such as /api/library, /api/usage and /api/notifications are excluded. Status 429 is recorded as warning, any other status of 400 or above as failed, and everything else as success. Logging happens after the response is produced and never blocks or fails a request.

See also

Was this article helpful?