Context.dev for the Gateway
Step-by-step guide: a Context.dev key (CONTEXT_API_KEY) on the Gateway for OpenCRM and OpenSEO's rendered audits: the account, the key, the check, rotation.
Context.dev is a hosted web scraping API: it loads a page in a browser, with
JavaScript rendering and anti-bot handling included, and returns the result.
The Gateway holds one Context.dev API key as CONTEXT_API_KEY and sends it
as a Bearer token in the Authorization header, to api.context.dev only,
for apps that list capabilities.context (access read). Open CRM requires
it (its research agent's brand data, LinkedIn profiles and website
extraction); OpenSEO lists it optional: a site audit with Render
JavaScript on renders pages through Context, and project setup reads
rate-limited Shopify store pages through it.
Deployed with everyapp deploy, OpenSEO has no browser of its own (the
manifest has no browser resource), so every page a rendered audit loads is
one Context credit; only OpenSEO's own Alchemy deploy binds Cloudflare Browser
Run and sends Context just the pages that browser can't render. At the end
the person has a Context.dev account (the Free plan is enough to start), one
key on the Gateway, Open CRM open, and OpenSEO's Render JavaScript toggle
enabled.
Settings
| NAME | required or optional | what it holds |
|---|---|---|
CONTEXT_API_KEY |
required by Open CRM; optional for OpenSEO (audits run without it; the Render JavaScript option says an admin sets it); a Gateway with neither leaves it unset | One Context.dev API key, exactly as the dashboard shows it (a key from the agent flow in step 2 begins with ctxt_secret_). The Gateway trims spaces around it and sends it as a Bearer token to api.context.dev only. |
Costs and plans
As of October 2026 (context.dev/pricing and docs.context.dev/account):
- Free: $0, no credit card, 1,000 credits a month (refilled monthly), 2 requests in flight at a time (pricing page and API reference; the billing docs' plan table says 1; the dashboard shows the organization's effective limit), 2 API keys per organization.
- Developer $19/month for 7,500 credits (10 in flight); Pro $99 for 125,000; Growth $299 for 500,000; Scale $499 for 1,000,000. Paid plans turn on Auto-Topup at checkout: below 1,000 credits it adds 1,000-credit blocks at the plan's rate ($2.20 per 1,000 on Developer).
- A basic scrape is 1 credit; OpenSEO counts 1 credit per rendered page (an audit with rendering stops at 1,000 pages). Failed requests (401, 403, 408, 429, 5xx, a site that blocks Context) are not billed; a page that answers 404 is.
- The usage call (the smoke check) costs 0 credits; 60 calls a minute.
- The limit that matters is concurrency: on Free, 1-2 renders run at once, so a large rendered audit is slow or sees 429s. On Free, a run of failed requests can also get every key in the organization disabled (401
DISABLED, below), not only slow the audit.
Only a person can
- Sign up or sign in at context.dev and accept its Terms of Service and Privacy Policy.
- Choose a paid plan or add a card (the Free plan needs neither); in the Context organization only an Owner or Admin can.
- Create the key in the dashboard and copy it; or open the setup link and enter the code in the agent flow (step 2).
- Type or export the key (the setup rules: the three ways). It never goes through the chat.
Before you start
pnpm everyapp secret list crm --json(Open CRM) orpnpm everyapp secret list openseo --json, from any directory: aCONTEXT_API_KEYentry withholdergatewayandsetupsetup/context.mdmeans this guide applies (in the checkout:landing-page/content/agent/setup/context.md). For Open CRM it is indata.app.setup.unmet(names["CONTEXT_API_KEY"],need.kindrequired) and the app doesn't open until it is set: the Gateway lists it as needing setup, sends a browser openinghttps://crm.<app base domain>to its setup dialog and answers anything else 503app_needs_setup;data.app.setup.commandispnpm everyapp configure crm, which asks for every missing setting (the OpenRouter key among them while that is missing;data.nextrepeats it),data.gateway.neededhas{ "name": "CONTEXT_API_KEY", "required": true, "set": false },data.gateway.missingnames it, andstatus(under "crm needs setup") andsecret list crm(under the unmet setting) printGuide: https://everyapp.com/docs/agent/setup/context.md: that is how an agent lands here. For OpenSEO it is indata.app.setup.settingswithneed.kindoptionalandset: false: the app opens,missingand the setup gate never name it (pnpm everyapp configure openseooffers it after the required ones), and its Render JavaScript toggle says an admin sets it. The entry with"set": truemeans it is set already: ask before replacing it (a value can't be read back, so "replace" means a new key).APP_NOT_FOUND("<app> isn't deployed to this Gateway yet"): that app isn't deployed; set the key anyway (pnpm everyapp secret list --jsonfrom theevery-appcheckout then shows it indata.gateway.names; in the app's directory the same command reads itseveryapp.config.tsand works before the first deploy too, as doespnpm everyapp configurethere) and run the app-level check after that deploy.pnpm everyapp status --json:data.installedanddata.claimedaretrue;data.gatewayUrlgives<gateway host>,data.appBaseDomainthe<app base domain>. The entry indata.appswith"id": "crm"(or"openseo"),"status": "active"and"bound": truecarries the samesetup;data.fixeshasAPP_NEEDS_SETUPfor crm (severitywarning, headline "crm needs setup",runthe same command) while a required setting is missing, never for OpenSEO on this key's account.- Ask whether the person already has a Context.dev account or key (a password manager, a variable in their shell). An existing key skips steps 2 and 3. Context allows one Free organization per person: with an account already, they sign in to it (and the agent flow must use that account's email).
- Ask how many pages a month they expect to render. More than 1,000 means a paid plan: their decision, never yours.
Steps
You: Run the two commands above. Tell the person in one line what they get (Open CRM's research agent: brand data, LinkedIn profiles and website extraction; rendered site audits in OpenSEO) and what it costs (free up to 1,000 rendered pages a month), ask the two questions in Before you start, and stop: their answers pick the path (an existing key: step 4 alone; an account: sign in, not up; the agent flow in step 2 only on their say-so). Then steps 2-4 go in one message, in order; wait once, after step 4.
Person: Sign up, or sign in. Page: https://www.context.dev/signup. Type their email in Email and click Continue (an email sign-up sets a password and asks them to verify the address by email), or pick Google or GitHub under "or continue with" (no verification step). Continuing accepts the Terms of Service and Privacy Policy. An existing account signs in from "Already have an account? Sign in" (https://context.dev/login). It worked when the dashboard opens.
Alternative, only with their say-so (it registers a key in their name): Context.dev's agent flow (context.dev/auth.md, read today, not run). Everything in one shell, with
EMAILset to the address they gave (an existing account's email, when they have one). Register:reg=$(jq -n --arg email "$EMAIL" '{type:"service_auth",login_hint:$email,api_key_name:"EveryApp Gateway",client_name:"everyapp setup"}' | curl -sS -X POST https://www.context.dev/agent/identity -H 'Content-Type: application/json' -d @-); printf '%s' "$reg" | jq '{claim, offer}'Show themclaim.verification_uriandclaim.user_codein full, and that both expire inclaim.expires_inseconds (600: ten minutes). If the response hasoffer, say it includes 1,000 extra credits (deadlineoffer.claim_by); promise nothing otherwise. A 429rate_limit_exceededfrom the registration means stop, no retry: step 3 instead. They open the link, enter the 6-digit code, and sign up or sign in with that email (a password sign-up also verifies the address). Then poll everyclaim.intervalseconds, for ten minutes at most, until the key arrives or an error ends it, and pipe the key straight in (the setup rules' "a command produced it");claim_tokenis read from$regin the same shell, never typed (it is short-lived and not the key):CLAIM=$(printf '%s' "$reg" | jq -r .claim_token); INTERVAL=$(printf '%s' "$reg" | jq -r '.claim.interval // 5'); for i in $(seq 1 120); do r=$(curl -sS -X POST https://www.context.dev/oauth2/token -d "grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=$CLAIM"); e=$(printf '%s' "$r" | jq -r '.error // empty'); case "$e" in '') break;; authorization_pending) ;; slow_down) sleep 10;; *) printf '%s' "$r" | jq '{error, error_description}'; break;; esac; sleep "$INTERVAL"; done; printf '%s' "$r" | jq -e .access_token >/dev/null && printf '%s' "$r" | jq -r .access_token | pnpm everyapp secret set CONTEXT_API_KEY --jsonauthorization_pendingmeans they haven't finished (email_verification_required: the address isn't verified yet);expired_tokenmeans the ten minutes passed: register again, only with their agreement;invalid_grantmeans stop and show itserror_description. When the loop ends without a key, nothing is set and the printed error says why (none printed: curl itself failed). Never print$r: the key is in it. This replaces steps 3 and 4: go to Verify (nobody but the Gateway holds the key now, so its smoke check is skipped; 1 and 3 there are the check).Person: Create the key. Page: https://context.dev/dashboard/api-keys. Create a new key with the page's button (its label isn't in the docs); if the dialog shows the key once, copy it before closing. Keep Full access (the default; it covers the Data APIs the apps use and the usage read the smoke check uses), or choose Restricted with Data APIs = Use and "Account logs, usage, and browser profiles" = Read. Name it "EveryApp Gateway" when the form asks (renaming later needs a paid plan). It worked when the key is listed on that page. A Free organization holds at most two keys: with two there already, delete one they no longer use (whether a disabled key still counts toward the two isn't in the docs), or use the agent flow above, which needs no button.
Person, then You: Put it on the Gateway. The setup rules' first way (the key is on a screen they are looking at): they open a second terminal in the
every-appfolder and runpnpm everyapp configure crm(orpnpm everyapp configure openseo; no--json: with it nothing prompts), which asks for this key among the app's other unset settings (for OpenSEO, which listscontextin an optional feature, configure namespnpm everyapp provider add contextunder that feature instead of asking; once an app is ready, configure asks nothing, so a key already set changes withsecret set CONTEXT_API_KEY), orpnpm everyapp secret set CONTEXT_API_KEYfor just this one. The prompt is hidden (CONTEXT_API_KEY, under lines saying what it is for and where it comes from;secret setasksValue for CONTEXT_API_KEY); they paste, press Enter, and seeSet CONTEXT_API_KEY on the Gateway(configure then sayscrm is ready.or what the app still needs, andLive now. No deploy needed.;secret setendsLive now on <gateway host>. No deploy needed.). Nothing is exported afterwards, so offer the smoke check (Verify 2, 0 credits) in that same terminal: the hidden read (the setup rules' third way:read -rs CONTEXT_API_KEY && export CONTEXT_API_KEY, paste, Enter), Verify 2's curl, they tell you its last line (200) andcredits_remaining, thenunset CONTEXT_API_KEY; declined, the app (Verify 3) is the check. The setup rules' third way from the start, when they read it in hidden instead (never a typedexport …=, which the shell history keeps; run the smoke check below first):pnpm everyapp configure <app id> --from-env --json, whosedoneevent hasdata.set["CONTEXT_API_KEY"]anddata.changed.gateway.names, orpnpm everyapp secret set CONTEXT_API_KEY --from-env CONTEXT_API_KEY --json, whosedoneevent hasdata.gateway.names["CONTEXT_API_KEY"]; thenunset CONTEXT_API_KEY. Never on a command line, never in the chat (a key seen in the chat gets rotated: step 3 again). Wait for "done".You: Verify, below. Neither app needs a restart or redeploy: the Gateway reads the new value on its next request.
Verify
pnpm everyapp secret list crm --jsonorpnpm everyapp secret list openseo --json, from anywhere: theCONTEXT_API_KEYentry indata.app.setup.settingshas"set": true; for Open CRM it has also leftdata.app.setup.unmet, andreadyis true once nothing else is missing (its OpenRouter key counts too; until thencommandanddata.nextname only what is left); for OpenSEO it was never inunmet(optional). In the older fields,data.gateway.nameslists it, itsdata.gateway.neededentry has"set": trueand it has leftdata.gateway.missing(before the app is deployed, the rootpnpm everyapp secret list --jsonshowsdata.gateway.namesand that is the check). Thenpnpm everyapp status --json: thecrmentry'ssetup.readyis true anddata.fixeshas noAPP_NEEDS_SETUPfor it, once everything else is set.- Smoke check, only while the key is exported in the person's terminal (the setup rules' third way, before
unset; or, after the hidden prompt, when they read it in hidden there for this check). After the agent flow nobody but the Gateway holds the key: skip to 3, which with 1 is the check. 0 credits;--config -keeps the key out of the process list:printf 'header = "Authorization: Bearer %s"\n' "$CONTEXT_API_KEY" | curl -sS --config - -w '\n%{http_code}\n' https://api.context.dev/v1/org/usageExpected:200on the last line, and JSON withcredits_remaining(1,000 on a fresh Free plan; 2,000 when the agent flow's gift applied) andrequest_id, always present;period.credits_usedandnext_refill.at(null on a paused account);key_metadata.credits_consumed, when present, 0. The body'serror_codeis what the If it fails table keys on: 401NOT_FOUND("API key not found") orUNAUTHORIZEDis the key, not the path, and an empty or unset variable gives the same 401;USAGE_EXCEEDED(402, or 401 on older deployments) is credits; 401DISABLEDa disabled key; 403INSUFFICIENT_PERMISSIONSa Restricted key without the usage read (OpenSEO still works when Data APIs is Use). The Gateway's proxy answers only over an app's binding, so no curl goes through it. - The app. Open CRM:
https://crm.<app base domain>opens instead of the setup dialog athttps://<gateway host>/?setup=crm(that dialog means a required setting is still unset: Verify 1 says which), and its research agent reads brand data, LinkedIn profiles and website pages through Context. OpenSEO: openhttps://openseo.<app base domain>(a browser sent tohttps://<gateway host>/?setup=openseoinstead means another required setting is unset: dataforseo.md, openrouter.md), a project, then Site Audit (the URL is/p/, the project's id,/audit). The Render JavaScript toggle is enabled, with no "needs a Context.dev API key" note (reload if the page was open). Start a small audit of a JavaScript-rendered site (a React or other single-page app) with it on; the audit lists the pages with their content. Each rendered page spends 1 credit: run the usage call again andperiod.credits_usedwent up, or open the key's usage link on the API keys page. For Open CRMpnpm everyapp status --jsonchecks this setting too: itsAPP_NEEDS_SETUPfor crm goes once every required setting is set (Verify 1); OpenSEO's never names it. - Logs: the Gateway logs every proxied call as
integration.proxy.callwith the app, path and status, and OpenSEO's own Worker logssite_audit:renderwithprovider: "context",httpStatus,creditsUsedandoutcome: the status only, never the body, so a 401'serror_codecomes from the smoke check, or from the key's usage link on the API keys page (its recent usage and request logs; whether those showerror_code: unverified). Cloudflare dashboard, the setup rules' path: Workers & Pages →every-app-gateway→ Observability (Logs on older accounts) → Live for a stream, or the stored Workers Logs on the same page (the Gateway's config turns them on).cfhas no terminal tail:cf o11y telemetry query --helpreads Workers Logs (read-only; filter on the Gateway's script) andcf o11y telemetry live-tailonly prepares a websocket, so the dashboard is the simpler path;npx wrangler tail every-app-gatewaystreams them live (wrangler isn't in the checkout: unverified).
If it fails
| error or symptom | meaning | what to do |
|---|---|---|
A browser opening the app is sent to https://<gateway host>/?setup=<app id>; any other request gets 503 with the body {"error":"app_needs_setup","app":"<app id>"} |
The Gateway's setup gate: a required setting is still unset (for OpenSEO the DataForSEO or OpenRouter key, never this one; for Open CRM, at /?setup=crm, this key or OpenRouter's) |
data.app.setup.command, pnpm everyapp configure <app id> (step 4), then Verify 1 |
integration_not_configured (503, header x-everyapp-error); OpenSEO's toggle disabled with "needs a Context.dev API key"; AUDIT_RENDERING_UNAVAILABLE |
The Gateway holds no CONTEXT_API_KEY (OpenSEO, which lists context in an optional feature, opens and shows these; Open CRM, which requires it, stays behind the setup gate instead: the row above) |
Step 4; check the name with secret list --json; reload OpenSEO (no restart: that note is for Docker) |
capability_not_requested (403) |
The deployed app's manifest doesn't list context |
Deploy a current OpenSEO or Open CRM |
integration_method_not_allowed (405), invalid_integration_route (400) |
A method other than GET or POST, or a host other than api.context.dev |
An app bug, not a key problem |
integration_upstream_failed (502) |
api.context.dev unreachable from the Gateway |
Retry later |
SECRET_VALUE_REQUIRED |
secret set --json ran with nothing piped and no terminal to ask in |
One of the three ways (step 4) |
SECRET_VALUE_REQUIRED from pnpm everyapp configure <app id> --json |
Nothing was piped (with --from-env, stdin isn't read), no variable of a missing name was found, and there is no terminal to ask in; the message names what the app still needs (for OpenSEO this key is optional, so it is never among them) |
The person's terminal (step 4), NAME=value lines on stdin, or the hidden read then its data.rerun, pnpm everyapp configure <app id> --from-env (which takes this key's variable too, as an unset optional setting) |
SECRET_NOT_DECLARED from pnpm everyapp configure <app id> --json |
A piped NAME=value line whose name the app's manifest doesn't declare (a typo of CONTEXT_API_KEY; the name is echoed only when it looks like one, else line <n> (not shown: it may be a value)); nothing was written |
The name is exactly CONTEXT_API_KEY: pipe the lines again (a bare value goes to pnpm everyapp secret set CONTEXT_API_KEY, not to configure; a line that isn't NAME=value at all is PIPED_LINE_INVALID) |
SECRET_SEVERAL_NAMES |
--from-env or a pipe with several names |
One name per call |
SECRET_EMPTY |
The --from-env variable is empty or unset |
They export it first, in the terminal that runs the command |
SECRET_NOT_DECLARED |
A typo (context_api_key, CONTEXTAPIKEY, another unknown name): data.didYouMean names the close one |
The name is exactly CONTEXT_API_KEY: rerun with it and the same value source (the pipe, or --from-env); data.rerun carries only the names, so run as is it stops with SECRET_VALUE_REQUIRED |
SECRET_NOT_SETTABLE |
One of setup's own names (GATEWAY_URL, BETTER_AUTH_SECRET, …), never this one |
Check the command: it named another setting |
SECRET_NAME_INVALID |
A character other than letters, digits and underscore | The name is exactly CONTEXT_API_KEY |
CONFIRMATION_REQUIRED |
secret unset --json without --yes: no terminal to confirm in |
Rerun with --yes only when the person asked |
Provider 401 with error_code NOT_FOUND ("API key not found") or UNAUTHORIZED (the smoke check; the Gateway's log shows the 401 status only) |
Key wrong, pasted incompletely, or an empty variable in the smoke check; not the path | New key (step 3), set again (step 4) |
Provider 401 DISABLED |
Context disabled the key; on Free, every key in the organization after repeated request errors (a rendered audit of an erroring site can do it) | Stop the audit; read the account email; inspect the key on https://context.dev/dashboard/api-keys and re-enable it once what was failing is fixed, or write support@context.dev. A new key alone doesn't fix the cause |
Provider USAGE_EXCEEDED (402; 401 on older deployments) |
Credits used up; the request isn't run or charged | Wait for error_data.next_refill_at, or the person upgrades or tops up at https://context.dev/dashboard/subscription (the URL the 402 body gives; a first paid plan from the pricing page's plan buttons). Only an Owner or Admin of the Context organization can; the billing docs say Auto-Topup, pauses and cancellation may need a message to Context |
Provider 403 INSUFFICIENT_PERMISSIONS |
A Restricted key without Data APIs = Use (or without the usage read: smoke check only) | A Full access key |
Provider 429 with Retry-After |
Concurrency limit (Free: 1-2 in flight), a per-minute limit, or authentication protection after repeated calls with a bad key | Not billed; smaller audits, or a higher plan; with a bad key, fix the key, don't retry. OpenSEO never retries a paid call by itself |
Provider 408, or output REQUEST_TIMEOUT |
The page didn't load within the 30 s OpenSEO allows | The site's speed, not the key; the audit goes on and logs outcome: "error" for that page |
Output WEBSITE_BLOCKED |
The site served an anti-bot wall to Context too | Not billed; nothing to fix on the Gateway |
action_required cloudflare_login (waiting: true) from any command |
No Cloudflare login, or it expired; the command logs in by itself | Show data.url and data.code to the person and keep reading; exit 2 means nobody approved in time: run the command again (pnpm everyapp login --json only to log in ahead of time) |
Rerun, rotate, remove
- Rerun: every step is safe to repeat.
configureandsecret setreplace the value in one new Gateway version; the dashboard shows the keys that exist (Free: two at most, so delete an unused one before creating a third). After an interruption start at Verify and continue at the first step that isn't done. - Rotate: the person creates a replacement key (step 3), sets it (step 4; live at once, no deploy), you verify, then they disable the old key from its action menu on https://context.dev/dashboard/api-keys. Rotate too when a teammate who copied the key leaves, or when a key was ever pasted in a chat.
- Remove:
pnpm everyapp secret unset CONTEXT_API_KEY --json --yes, and--yesonly when the person asked (with--jsonand no--yesthere is no terminal to confirm in:CONFIRMATION_REQUIRED). Itsdoneevent'sdata.gateway.nameslists the removed name; a name the Gateway doesn't hold endsdonewithdata.removed: falseand "isn't set on the Gateway. Nothing changed." Then they disable or delete the key in the dashboard: a key the Gateway no longer holds is still a key. Open CRM then needs setup again: the Gateway closes it until the key is back (statussays so, with this guide's path). OpenSEO keeps opening, with the Render JavaScript toggle back to disabled and audits without rendering working.
Sources
- https://www.context.dev (what it is, plans) and https://www.context.dev/pricing (prices, credits per operation, in-flight limits per plan, "any plan returns 401 USAGE_EXCEEDED" when credits run out)
- https://www.context.dev/signup and https://www.context.dev/login (the forms' fields and links), https://www.context.dev/auth.md and https://docs.context.dev/agent-quickstart.md (the agent key flow: registration errors, the poll's errors and interval, the signup gift)
- https://docs.context.dev (base URL, Bearer auth), https://docs.context.dev/account/api-keys.md (dashboard page, Full access and Restricted, key limits, rotation), https://docs.context.dev/account/credits.md (credit costs, what isn't billed, one unpaid organization per person), https://docs.context.dev/account/billing.md (allowances, Auto-Topup, 402 and its
info_url, what the dashboard can't change, Free keys disabled after request errors), https://docs.context.dev/account/team.md (which roles manage billing), https://docs.context.dev/optimization/rate-limits.md (concurrency, 429 headers, authentication protection), https://docs.context.dev/optimization/troubleshooting.md (401NOT_FOUND,DISABLED, the 429 causes), https://docs.context.dev/api-reference/usage/retrieve.md (GET /v1/org/usage: 0 credits,logs:read, which fields are required or null), https://docs.context.dev/api-reference/web-scraping/scrape.md (POST /v1/web/scrape, status codes, the legacy 401), https://docs.context.dev/scrape/timeouts-and-errors.md (output error codes) - https://developers.cloudflare.com/workers/observability/logs/workers-logs/ (the dashboard path);
cf o11y telemetry --helpandcf cli search tail(cf 1.0.0-beta.14) - Probed today without a key:
GET https://api.context.dev/v1/web/scrape/html(the path OpenSEO calls; not in today's docs, but it answers 401NOT_FOUND"No API key provided" while an unknown path answers 404ENDPOINT_NOT_FOUND, so it still exists),GET /v1/org/usage(the same 401 body; with a bogus key, 401NOT_FOUND"API key not found"), andPOST https://www.context.dev/oauth2/tokenwith an unknown claim token (400invalid_grant, noaccess_token) - In code:
packages/sdk/src/internal/capabilities.ts(host, Bearer, trim, GET and POST),apps/every-app-gateway/src/server/integration-proxy.ts(the Gateway's codes; its log line carries the status, not the body),packages/cli/src/lib/secretValue.tsandpackages/cli/src/commands/secret/index.ts(theSECRET_*codes,secret list <app>from anywhere,data.app.setup, the keptdata.gateway.neededandmissing,data.next,data.removed,APP_NOT_FOUND),packages/cli/src/commands/configure/index.ts(configure <app>: the prompts,NAME=valuelines on stdin,--from-env,SECRET_VALUE_REQUIRED,SECRET_NOT_DECLARED),packages/sdk/src/internal/appSettings.ts(AppSetup:unmet,settings,groups,command;setup/context.mdfrom the capability registry,packages/sdk/src/internal/capabilities.ts),packages/cli/src/lib/app/settings.ts(theGuide:line),packages/cli/src/commands/status/problems.ts(APP_NEEDS_SETUP),apps/every-app-gateway/src/server/app-needs-setup.ts(the setup gate: the redirect to/?setup=<app id>and 503app_needs_setup),packages/cli/src/lib/output/prompt.ts(CONFIRMATION_REQUIRED),packages/perimeter/src/manifest/manifest.ts(what a manifest can bind: no browser); OpenSEO'ssrc/server/lib/audit/rendered-page.ts,rendering-policy.ts,src/server/lib/provider-access.ts,src/shared/audit-limits.ts,everyapp.config.ts(contextoptional, with itswhy; Open CRM's marks it required) anddocs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md(all in OpenSEO's repository)
DataForSEO for the Gateway
Step-by-step guide: a DataForSEO login (DATAFORSEO_API_KEY) on the Gateway for OpenSEO and the DataForSEO MCP app: the account and its balance, the key, the check, rotation.
A remote MCP server as an MCP app
Step-by-step guide: add any remote MCP server by URL as an MCP app (everyapp mcp add): no credential, a static key (<ID>_MCP_TOKEN) or OAuth with each member connecting; check, refresh, remove.