everyapp 0.2.2 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -77
- package/bin/everyapp.js +235 -0
- package/dist/access-F3GKBKAW.js +1 -0
- package/dist/activity-4MFMH5JP.js +12 -0
- package/dist/apps-63K2DGVT.js +9 -0
- package/dist/chunk-2UFJRKEE.js +4 -0
- package/dist/chunk-3AAHHUJC.js +11 -0
- package/dist/chunk-3Z53CH4F.js +6 -0
- package/dist/chunk-5ZTFC6DK.js +1 -0
- package/dist/chunk-6O3GJS5H.js +15 -0
- package/dist/chunk-74EST4VW.js +2 -0
- package/dist/chunk-7LG3F4FF.js +10 -0
- package/dist/chunk-AUDGOIHE.js +1 -0
- package/dist/chunk-BHMRY53V.js +2 -0
- package/dist/chunk-DQWOXSL3.js +32 -0
- package/dist/chunk-E3EPK27P.js +10 -0
- package/dist/chunk-E7EUXUVD.js +1 -0
- package/dist/chunk-ECLJ5IAL.js +126 -0
- package/dist/chunk-EQX4GKJ2.js +4 -0
- package/dist/chunk-EUNGJMJ6.js +20 -0
- package/dist/chunk-F6IH47GT.js +4 -0
- package/dist/chunk-G64S77GC.js +33 -0
- package/dist/chunk-GD2JZMD3.js +1 -0
- package/dist/chunk-GRFPP4UW.js +7 -0
- package/dist/chunk-IA534DGW.js +10 -0
- package/dist/chunk-IG26HYRR.js +45 -0
- package/dist/chunk-IZEOAOKL.js +6 -0
- package/dist/chunk-JYKRUS5C.js +1 -0
- package/dist/chunk-KMAOZRV4.js +18 -0
- package/dist/chunk-MC56CUQI.js +7 -0
- package/dist/chunk-MDORVBPD.js +1 -0
- package/dist/chunk-N4E5YE4X.js +5 -0
- package/dist/chunk-NJFWEYX7.js +1 -0
- package/dist/chunk-OEOVNJAN.js +1 -0
- package/dist/chunk-OVKVV6MK.js +2 -0
- package/dist/chunk-PL6EORDJ.js +15 -0
- package/dist/chunk-PMPIMHKG.js +6 -0
- package/dist/chunk-QZWSSWMP.js +22 -0
- package/dist/chunk-R4CGF622.js +2 -0
- package/dist/chunk-RFVMR32Y.js +1 -0
- package/dist/chunk-RQFHVU3Y.js +25 -0
- package/dist/chunk-SAT3PGZU.js +57 -0
- package/dist/chunk-SPPR6WCI.js +1 -0
- package/dist/chunk-TPWSGRNT.js +30 -0
- package/dist/chunk-VVQRET35.js +2 -0
- package/dist/chunk-VXJQGJ7Y.js +2 -0
- package/dist/chunk-WEM3WLOC.js +1 -0
- package/dist/chunk-Y3VIFPN5.js +1 -0
- package/dist/chunk-YJBYZWSP.js +22 -0
- package/dist/chunk-ZYL2ZHCF.js +98 -0
- package/dist/configure-2KMHM22T.js +8 -0
- package/dist/create-ELEJMZBS.js +22 -0
- package/dist/db-MZIAKGBN.js +9 -0
- package/dist/delete-H47KPJSB.js +3 -0
- package/dist/deploy-HGFJLLUO.js +1 -0
- package/dist/dev-62VA7UEM.js +86 -0
- package/dist/email-NNFD3VVB.js +10 -0
- package/dist/events-JDY76EG6.js +15 -0
- package/dist/groups-NQFXDZZP.js +8 -0
- package/dist/index.js +92 -1
- package/dist/install-JTZWRZIL.js +1 -0
- package/dist/login-P6LYQQWP.js +1 -0
- package/dist/mcp-NCFWEBMD.js +1 -0
- package/dist/provider-WVBI2VED.js +13 -0
- package/dist/release-6FYSPXXA.js +1 -0
- package/dist/restore-XBU7DOJV.js +3 -0
- package/dist/secret-ML5EXS3O.js +1 -0
- package/dist/setup-IEXCISLP.js +1 -0
- package/dist/status-OPLPKJ4U.js +4 -0
- package/dist/teardown-3JMMLK6O.js +22 -0
- package/dist/update-37ZDATLR.js +1 -0
- package/dist/workspace-DAFZVYQ2.js +21 -0
- package/package.json +30 -27
- package/skills/build-every-app/SKILL.md +199 -0
- package/skills/build-every-app/patterns.md +134 -0
- package/skills/everyapp/SKILL.md +292 -0
- package/skills/everyapp/mcp.md +336 -0
- package/skills/everyapp/security-review.md +61 -0
- package/skills/everyapp/setup/README.md +263 -0
- package/skills/everyapp/setup/access.md +178 -0
- package/skills/everyapp/setup/context.md +117 -0
- package/skills/everyapp/setup/dataforseo.md +287 -0
- package/skills/everyapp/setup/email.md +366 -0
- package/skills/everyapp/setup/gateway.md +243 -0
- package/skills/everyapp/setup/google.md +360 -0
- package/skills/everyapp/setup/microsoft-365.md +281 -0
- package/skills/everyapp/setup/openai.md +318 -0
- package/skills/everyapp/setup/openrouter.md +309 -0
- package/skills/everyapp/setup/providers.md +353 -0
- package/skills/everyapp/setup/remote-mcp.md +199 -0
- package/skills/everyapp/setup/slack.md +344 -0
- package/skills/everyapp/setup/workers-ai.md +138 -0
- package/bin/__everyapp_bash_complete.js +0 -12
- package/dist/bash-complete.js +0 -3
- package/dist/chunk-7BOQJFI5.js +0 -8
- package/dist/chunk-7YVJFS4A.js +0 -1
- package/dist/chunk-BIXKPCDH.js +0 -2
- package/dist/chunk-DLGPLHBH.js +0 -47
- package/dist/chunk-FD6K2YA7.js +0 -9
- package/dist/chunk-GAJEQYJH.js +0 -39
- package/dist/chunk-O2KDOP5R.js +0 -13
- package/dist/chunk-OI2MBGBG.js +0 -4
- package/dist/chunk-POSDLD3G.js +0 -8
- package/dist/chunk-RB5D2R45.js +0 -1
- package/dist/chunk-U4LIQ5VR.js +0 -1
- package/dist/chunk-VEYSNPDH.js +0 -3
- package/dist/chunk-VP6SKW44.js +0 -7
- package/dist/chunk-WO3NE7ZU.js +0 -7
- package/dist/chunk-XTO2VTPY.js +0 -6
- package/dist/chunk-YJ2DKIZS.js +0 -1
- package/dist/create-DYQ3NBWJ.js +0 -22
- package/dist/deploy-PYTCMDGV.js +0 -7
- package/dist/deploy-RMAHOA4Z.js +0 -55
- package/dist/dev-SAE237FS.js +0 -0
- package/dist/generateConfig-46Z3ERF3.js +0 -1
- package/dist/login-B6EF3SMX.js +0 -1
- package/dist/logout-XMKZBLNV.js +0 -1
- package/dist/remoteD1Shell-A67JMO3H.js +0 -6
- package/dist/setupLocal-TZJUW5ES.js +0 -6
- package/gateway/every-app-gateway-build.tar.gz +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Context.dev for the Gateway
|
|
2
|
+
|
|
3
|
+
Context.dev is a hosted web scraping API: it loads a page in a browser, with
|
|
4
|
+
JavaScript rendering and anti-bot handling included, and returns the result.
|
|
5
|
+
The Gateway holds one Context.dev API key as `CONTEXT_API_KEY` and sends it
|
|
6
|
+
as a Bearer token in the `Authorization` header, to `api.context.dev` only,
|
|
7
|
+
for apps that list `capabilities.context` (access `read`). Open CRM requires
|
|
8
|
+
it (its research agent's brand data, LinkedIn profiles and website
|
|
9
|
+
extraction); OpenSEO lists it optional: a site audit with **Render
|
|
10
|
+
JavaScript** on renders pages through Context, and project setup reads
|
|
11
|
+
rate-limited Shopify store pages through it.
|
|
12
|
+
Deployed with `everyapp deploy`, OpenSEO has no browser of its own (the
|
|
13
|
+
manifest has no browser resource), so every page a rendered audit loads is
|
|
14
|
+
one Context credit; only OpenSEO's own Alchemy deploy binds Cloudflare Browser
|
|
15
|
+
Run and sends Context just the pages that browser can't render. At the end
|
|
16
|
+
the person has a Context.dev account (the Free plan is enough to start), one
|
|
17
|
+
key on the Gateway, Open CRM open, and OpenSEO's **Render JavaScript** toggle
|
|
18
|
+
enabled.
|
|
19
|
+
|
|
20
|
+
## Settings
|
|
21
|
+
|
|
22
|
+
| NAME | required or optional | what it holds |
|
|
23
|
+
| ----------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| `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. |
|
|
25
|
+
|
|
26
|
+
## Costs and plans
|
|
27
|
+
|
|
28
|
+
As of October 2026 (context.dev/pricing and docs.context.dev/account):
|
|
29
|
+
|
|
30
|
+
- 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.
|
|
31
|
+
- 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).
|
|
32
|
+
- 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.
|
|
33
|
+
- The usage call (the smoke check) costs 0 credits; 60 calls a minute.
|
|
34
|
+
- 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.
|
|
35
|
+
|
|
36
|
+
## Only a person can
|
|
37
|
+
|
|
38
|
+
- Sign up or sign in at context.dev and accept its Terms of Service and Privacy Policy.
|
|
39
|
+
- Choose a paid plan or add a card (the Free plan needs neither); in the Context organization only an Owner or Admin can.
|
|
40
|
+
- Create the key in the dashboard and copy it; or open the setup link and enter the code in the agent flow (step 2).
|
|
41
|
+
- Type or export the key (README: the three ways). It never goes through the chat.
|
|
42
|
+
|
|
43
|
+
## Before you start
|
|
44
|
+
|
|
45
|
+
- `pnpm everyapp secret list crm --json` (Open CRM) or `pnpm everyapp secret list openseo --json`, from any directory: a `CONTEXT_API_KEY` entry with `holder` `gateway` and `setup` `setup/context.md` means this file applies (in the checkout: `packages/cli/skills/everyapp/setup/context.md`). For Open CRM it is in `data.app.setup.unmet` (`names` `["CONTEXT_API_KEY"]`, `need.kind` `required`) and the app doesn't open until it is set: the Gateway lists it as needing setup, sends a browser opening `https://crm.<app base domain>` to its setup dialog and answers anything else 503 `app_needs_setup`; `data.app.setup.command` is `pnpm everyapp configure crm`, which asks for every missing setting (the OpenRouter key among them while that is missing; `data.next` repeats it), `data.gateway.needed` has `{ "name": "CONTEXT_API_KEY", "required": true, "set": false }`, `data.gateway.missing` names it, and `status` (under "crm needs setup") and `secret list crm` (under the unmet setting) print `Guide: .claude/skills/everyapp/setup/context.md`: that is how an agent lands here. For OpenSEO it is in `data.app.setup.settings` with `need.kind` `optional` and `set: false`: the app opens, `missing` and the setup gate never name it (`pnpm everyapp configure openseo` offers it after the required ones), and its **Render JavaScript** toggle says an admin sets it. The entry with `"set": true` means 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 --json` from the `every-app` checkout then shows it in `data.gateway.names`; in the app's directory the same command reads its `everyapp.config.ts` and works before the first deploy too, as does `pnpm everyapp configure` there) and run the app-level check after that deploy.
|
|
46
|
+
- `pnpm everyapp status --json`: `data.installed` and `data.claimed` are `true`; `data.gatewayUrl` gives `<gateway host>`, `data.appBaseDomain` the `<app base domain>`. The entry in `data.apps` with `"id": "crm"` (or `"openseo"`), `"status": "active"` and `"bound": true` carries the same `setup`; `data.fixes` has `APP_NEEDS_SETUP` for crm (severity `warning`, headline "crm needs setup", `run` the same command) while a required setting is missing, never for OpenSEO on this key's account.
|
|
47
|
+
- 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).
|
|
48
|
+
- Ask how many pages a month they expect to render. More than 1,000 means a paid plan: their decision, never yours.
|
|
49
|
+
|
|
50
|
+
## Steps
|
|
51
|
+
|
|
52
|
+
1. **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.
|
|
53
|
+
|
|
54
|
+
2. **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.
|
|
55
|
+
|
|
56
|
+
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 `EMAIL` set to the address they gave (an existing account's email, when they have one). Register:
|
|
57
|
+
`reg=$(jq -n --arg email "$EMAIL" '{type:"service_auth",login_hint:$email,api_key_name:"Every App 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}'`
|
|
58
|
+
Show them `claim.verification_uri` and `claim.user_code` in full, and that both expire in `claim.expires_in` seconds (600: ten minutes). If the response has `offer`, say it includes 1,000 extra credits (deadline `offer.claim_by`); promise nothing otherwise. A 429 `rate_limit_exceeded` from 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 every `claim.interval` seconds, for ten minutes at most, until the key arrives or an error ends it, and pipe the key straight in (README's "a command produced it"); `claim_token` is read from `$reg` in the same shell, never typed (it is short-lived and not the key):
|
|
59
|
+
`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 --json`
|
|
60
|
+
`authorization_pending` means they haven't finished (`email_verification_required`: the address isn't verified yet); `expired_token` means the ten minutes passed: register again, only with their agreement; `invalid_grant` means stop and show its `error_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).
|
|
61
|
+
|
|
62
|
+
3. **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 "Every App 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.
|
|
63
|
+
|
|
64
|
+
4. **Person, then You:** Put it on the Gateway. README's first way (the key is on a screen they are looking at): they open a second terminal in the `every-app` folder and run `pnpm everyapp configure crm` (or `pnpm everyapp configure openseo`; no `--json`: with it nothing prompts), which asks for this key among the app's other unset settings (for OpenSEO it is optional: Yes to `Set the N optional settings too?` (`Set the optional setting too?` for one), then `CONTEXT_API_KEY (Enter to skip)`; once an app is ready, its menu `Everything <app> needs is set. Change one?` lists it), or `pnpm everyapp secret set CONTEXT_API_KEY` for just this one. The prompt is hidden (`CONTEXT_API_KEY`, under lines saying what it is for and where it comes from; `secret set` asks `Value for CONTEXT_API_KEY`); they paste, press Enter, and see `Set CONTEXT_API_KEY on the Gateway` (configure then says `crm is ready.` or what the app still needs, and `Live now. No deploy needed.`; `secret set` ends `Live 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 (README's third way: `read -rs CONTEXT_API_KEY && export CONTEXT_API_KEY`, paste, Enter), Verify 2's curl, they tell you its last line (200) and `credits_remaining`, then `unset CONTEXT_API_KEY`; declined, the app (Verify 3) is the check. README's third way from the start, when they read it in hidden instead (never a typed `export …=`, which the shell history keeps; run the smoke check below first): `pnpm everyapp configure <app id> --from-env --json`, whose `done` event has `data.set` `["CONTEXT_API_KEY"]` and `data.changed.gateway.names`, or `pnpm everyapp secret set CONTEXT_API_KEY --from-env CONTEXT_API_KEY --json`, whose `done` event has `data.gateway.names` `["CONTEXT_API_KEY"]`; then `unset 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".
|
|
65
|
+
|
|
66
|
+
5. **You:** Verify, below. Neither app needs a restart or redeploy: the Gateway reads the new value on its next request.
|
|
67
|
+
|
|
68
|
+
## Verify
|
|
69
|
+
|
|
70
|
+
1. `pnpm everyapp secret list crm --json` or `pnpm everyapp secret list openseo --json`, from anywhere: the `CONTEXT_API_KEY` entry in `data.app.setup.settings` has `"set": true`; for Open CRM it has also left `data.app.setup.unmet`, and `ready` is true once nothing else is missing (its OpenRouter key counts too; until then `command` and `data.next` name only what is left); for OpenSEO it was never in `unmet` (optional). In the older fields, `data.gateway.names` lists it, its `data.gateway.needed` entry has `"set": true` and it has left `data.gateway.missing` (before the app is deployed, the root `pnpm everyapp secret list --json` shows `data.gateway.names` and that is the check). Then `pnpm everyapp status --json`: the `crm` entry's `setup.ready` is true and `data.fixes` has no `APP_NEEDS_SETUP` for it, once everything else is set.
|
|
71
|
+
2. Smoke check, only while the key is exported in the person's terminal (README's 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:
|
|
72
|
+
`printf 'header = "Authorization: Bearer %s"\n' "$CONTEXT_API_KEY" | curl -sS --config - -w '\n%{http_code}\n' https://api.context.dev/v1/org/usage`
|
|
73
|
+
Expected: `200` on the last line, and JSON with `credits_remaining` (1,000 on a fresh Free plan; 2,000 when the agent flow's gift applied) and `request_id`, always present; `period.credits_used` and `next_refill.at` (null on a paused account); `key_metadata.credits_consumed`, when present, 0. The body's `error_code` is what the If it fails table keys on: 401 `NOT_FOUND` ("API key not found") or `UNAUTHORIZED` is 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; 401 `DISABLED` a disabled key; 403 `INSUFFICIENT_PERMISSIONS` a 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.
|
|
74
|
+
3. The app. Open CRM: `https://crm.<app base domain>` opens instead of the setup dialog at `https://<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: open `https://openseo.<app base domain>` (a browser sent to `https://<gateway host>/?setup=openseo` instead means another required setting is unset: [dataforseo.md](dataforseo.md), [openrouter.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 and `period.credits_used` went up, or open the key's usage link on the API keys page. For Open CRM `pnpm everyapp status --json` checks this setting too: its `APP_NEEDS_SETUP` for crm goes once every required setting is set (Verify 1); OpenSEO's never names it.
|
|
75
|
+
4. Logs: the Gateway logs every proxied call as `integration.proxy.call` with the app, path and status, and OpenSEO's own Worker logs `site_audit:render` with `provider: "context"`, `httpStatus`, `creditsUsed` and `outcome`: the status only, never the body, so a 401's `error_code` comes from the smoke check, or from the key's usage link on the API keys page (its recent usage and request logs; whether those show `error_code`: unverified). Cloudflare dashboard, README's 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). `cf` has no terminal tail: `cf o11y telemetry query --help` reads Workers Logs (read-only; filter on the Gateway's script) and `cf o11y telemetry live-tail` only prepares a websocket, so the dashboard is the simpler path; `npx wrangler tail every-app-gateway` streams them live (wrangler isn't in the checkout: unverified).
|
|
76
|
+
|
|
77
|
+
## If it fails
|
|
78
|
+
|
|
79
|
+
| error or symptom | meaning | what to do |
|
|
80
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
81
|
+
| 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 |
|
|
82
|
+
| `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 marks `context` optional, 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) |
|
|
83
|
+
| `capability_not_requested` (403) | The deployed app's manifest doesn't list `context` | Deploy a current OpenSEO or Open CRM |
|
|
84
|
+
| `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 |
|
|
85
|
+
| `integration_upstream_failed` (502) | `api.context.dev` unreachable from the Gateway | Retry later |
|
|
86
|
+
| `SECRET_VALUE_REQUIRED` | `secret set --json` ran with nothing piped and no terminal to ask in | One of the three ways (step 4) |
|
|
87
|
+
| `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) |
|
|
88
|
+
| `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`) |
|
|
89
|
+
| `SECRET_SEVERAL_NAMES` | `--from-env` or a pipe with several names | One name per call |
|
|
90
|
+
| `SECRET_EMPTY` | The `--from-env` variable is empty or unset | They export it first, in the terminal that runs the command |
|
|
91
|
+
| `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` |
|
|
92
|
+
| `SECRET_NOT_SETTABLE` | One of setup's own names (`GATEWAY_URL`, `BETTER_AUTH_SECRET`, …), never this one | Check the command: it named another setting |
|
|
93
|
+
| `SECRET_NAME_INVALID` | A character other than letters, digits and underscore | The name is exactly `CONTEXT_API_KEY` |
|
|
94
|
+
| `CONFIRMATION_REQUIRED` | `secret unset --json` without `--yes`: no terminal to confirm in | Rerun with `--yes` only when the person asked |
|
|
95
|
+
| 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) |
|
|
96
|
+
| 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 |
|
|
97
|
+
| 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 |
|
|
98
|
+
| Provider 403 `INSUFFICIENT_PERMISSIONS` | A Restricted key without Data APIs = Use (or without the usage read: smoke check only) | A Full access key |
|
|
99
|
+
| 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 |
|
|
100
|
+
| 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 |
|
|
101
|
+
| Output `WEBSITE_BLOCKED` | The site served an anti-bot wall to Context too | Not billed; nothing to fix on the Gateway |
|
|
102
|
+
| `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) |
|
|
103
|
+
|
|
104
|
+
## Rerun, rotate, remove
|
|
105
|
+
|
|
106
|
+
- Rerun: every step is safe to repeat. `configure` and `secret set` replace 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.
|
|
107
|
+
- 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.
|
|
108
|
+
- Remove: `pnpm everyapp secret unset CONTEXT_API_KEY --json --yes`, and `--yes` only when the person asked (with `--json` and no `--yes` there is no terminal to confirm in: `CONFIRMATION_REQUIRED`). Its `done` event's `data.gateway.names` lists the removed name; a name the Gateway doesn't hold ends `done` with `data.removed: false` and "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 (`status` says so, with this file's path). OpenSEO keeps opening, with the **Render JavaScript** toggle back to disabled and audits without rendering working.
|
|
109
|
+
|
|
110
|
+
## Sources
|
|
111
|
+
|
|
112
|
+
- 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)
|
|
113
|
+
- 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)
|
|
114
|
+
- 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 (401 `NOT_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)
|
|
115
|
+
- https://developers.cloudflare.com/workers/observability/logs/workers-logs/ (the dashboard path); `cf o11y telemetry --help` and `cf cli search tail` (cf 1.0.0-beta.14)
|
|
116
|
+
- 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 401 `NOT_FOUND` "No API key provided" while an unknown path answers 404 `ENDPOINT_NOT_FOUND`, so it still exists), `GET /v1/org/usage` (the same 401 body; with a bogus key, 401 `NOT_FOUND` "API key not found"), and `POST https://www.context.dev/oauth2/token` with an unknown claim token (400 `invalid_grant`, no `access_token`)
|
|
117
|
+
- 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.ts` and `packages/cli/src/commands/secret/index.ts` (the `SECRET_*` codes, `secret list <app>` from anywhere, `data.app.setup`, the kept `data.gateway.needed` and `missing`, `data.next`, `data.removed`, `APP_NOT_FOUND`), `packages/cli/src/commands/configure/index.ts` (`configure <app>`: the prompts, `NAME=value` lines on stdin, `--from-env`, `SECRET_VALUE_REQUIRED`, `SECRET_NOT_DECLARED`), `packages/sdk/src/internal/appSettings.ts` (`AppSetup`: `unmet`, `settings`, `groups`, `command`; `setup/context.md` from the capability registry, `packages/sdk/src/internal/capabilities.ts`), `packages/cli/src/lib/app/settings.ts` (the `Guide:` 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 503 `app_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's `src/server/lib/audit/rendered-page.ts`, `rendering-policy.ts`, `src/server/lib/provider-access.ts`, `src/shared/audit-limits.ts`, `everyapp.config.ts` (`context` optional, with its `why`; Open CRM's marks it required) and `docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md` (all in OpenSEO's repository)
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# DataForSEO for the Gateway
|
|
2
|
+
|
|
3
|
+
DataForSEO is the SEO data provider behind OpenSEO: keyword metrics, SERPs,
|
|
4
|
+
backlinks, local business and AI answer data, and scheduled rank checks.
|
|
5
|
+
The Gateway holds one DataForSEO credential (`DATAFORSEO_API_KEY`) and puts
|
|
6
|
+
it on every call OpenSEO makes to `api.dataforseo.com` (or the free
|
|
7
|
+
`sandbox.dataforseo.com`); the app never sees it. At the end the person has
|
|
8
|
+
a DataForSEO account with credit on it, the Gateway holds the credential,
|
|
9
|
+
and a keyword lookup in OpenSEO returns real numbers.
|
|
10
|
+
|
|
11
|
+
## Settings
|
|
12
|
+
|
|
13
|
+
| NAME | required or optional | what it holds |
|
|
14
|
+
| -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| `DATAFORSEO_API_KEY` | required by OpenSEO and by the DataForSEO MCP app | `login:password`: the API login and API password from the dashboard's API Access page, joined by a colon. The base64 of that string works too. |
|
|
16
|
+
|
|
17
|
+
The API password is generated by DataForSEO and is not the account's
|
|
18
|
+
sign-in password. The Gateway base64-encodes a value containing a colon and
|
|
19
|
+
sends the result as HTTP Basic auth; a value without a colon is sent as is.
|
|
20
|
+
|
|
21
|
+
The same credential serves the DataForSEO MCP app, which offers
|
|
22
|
+
DataForSEO's tools to members' AI clients through the Gateway's `/mcp` with
|
|
23
|
+
no page and nothing to deploy: `pnpm everyapp mcp on dataforseo --json` turns
|
|
24
|
+
it on (before or after the key is set; `mcp list` shows both), and members an
|
|
25
|
+
admin grants see `dataforseo_<tool>` tools in their connected client.
|
|
26
|
+
|
|
27
|
+
## Costs and plans
|
|
28
|
+
|
|
29
|
+
As of October 2026: a free account gets a $1 trial credit on registration,
|
|
30
|
+
with no time limit, and the Sandbox is free for any registered user (it
|
|
31
|
+
answers like production with made-up data). Real data is pay as you go, no
|
|
32
|
+
subscription; the minimum top-up is $50 and funds do not expire. Daily
|
|
33
|
+
spending limits can be set per account or per endpoint (API Settings). The
|
|
34
|
+
docs state 10,000 API calls per minute and 30 simultaneous requests (the
|
|
35
|
+
older FAQ still says 2,000 per minute); the account's own limits are in the
|
|
36
|
+
`user_data` response. Each keyword lookup costs a few cents: a Google Ads
|
|
37
|
+
search-volume request is $0.09 live, a DataForSEO Labs request starts at
|
|
38
|
+
$0.012 plus $0.00012 per result, a live SERP is $0.002. Since 2026-07-01 the
|
|
39
|
+
Backlinks API is pay as you go like the rest (DataForSEO removed its $100
|
|
40
|
+
monthly commitment; prices went up 20%). No card is asked for at sign-up
|
|
41
|
+
(dataforseo.com/apis/serp-api: "No credit card required"); a card or other
|
|
42
|
+
payment comes in only at Add Funds.
|
|
43
|
+
|
|
44
|
+
## Only a person can
|
|
45
|
+
|
|
46
|
+
- Register the DataForSEO account and accept its terms.
|
|
47
|
+
- Read the API login and API password off the API Access page (or their
|
|
48
|
+
email) and type them at the hidden prompt.
|
|
49
|
+
- Add funds, pick a daily spending limit, or clear an old IP Access list.
|
|
50
|
+
- Decide to replace a credential the Gateway already holds.
|
|
51
|
+
|
|
52
|
+
## Before you start
|
|
53
|
+
|
|
54
|
+
1. `pnpm everyapp secret list openseo --json`, from any directory (the app
|
|
55
|
+
id is `openseo`; a fork may have changed the manifest's `id`): the
|
|
56
|
+
`DATAFORSEO_API_KEY` entry in `data.app.setup.unmet` (`names`,
|
|
57
|
+
`need.kind` `required`, `holder` `gateway`, `setup`
|
|
58
|
+
`setup/dataforseo.md`) means this file applies, and OpenSEO doesn't open
|
|
59
|
+
until it is set: the Gateway lists it as needing setup, sends a browser
|
|
60
|
+
opening `https://openseo.<app base domain>` to its setup dialog and
|
|
61
|
+
answers anything else 503 `app_needs_setup`. `data.app.setup.command` is
|
|
62
|
+
`pnpm everyapp configure openseo`, which asks for every missing setting
|
|
63
|
+
(`data.next` repeats it; the OpenRouter key is among them while it is
|
|
64
|
+
missing: [openrouter.md](openrouter.md); Context is optional for
|
|
65
|
+
OpenSEO, so `CONTEXT_API_KEY` sits in `data.app.setup.settings` with
|
|
66
|
+
`set: false` and never in `unmet`, and configure offers it after the
|
|
67
|
+
required ones: [context.md](context.md)). The same entry in
|
|
68
|
+
`data.app.setup.settings` with `"set": true` means it is set already: ask
|
|
69
|
+
before replacing; a value can't be read back, so "replace" means the
|
|
70
|
+
person fetches the credential again. The older fields still say the same:
|
|
71
|
+
`data.gateway.needed` has `{ "name": "DATAFORSEO_API_KEY", "required":
|
|
72
|
+
true, "set": … }` and `data.gateway.missing` names it while unset. This
|
|
73
|
+
is how an agent lands here: `status` (under "openseo needs setup") and
|
|
74
|
+
`secret list openseo` (under the unmet setting) print `Guide:
|
|
75
|
+
.claude/skills/everyapp/setup/dataforseo.md`, and `secret list --json`
|
|
76
|
+
carries the path as the entry's `setup` (in the checkout the file is
|
|
77
|
+
`packages/cli/skills/everyapp/setup/dataforseo.md`). The error
|
|
78
|
+
`APP_NOT_FOUND` ("openseo isn't deployed to this Gateway yet", exit 1):
|
|
79
|
+
deploy it first. OpenSEO lives in its own repository, which publishes a
|
|
80
|
+
release on every push: `pnpm everyapp install
|
|
81
|
+
https://github.com/every-app/open-seo --json` from this checkout installs
|
|
82
|
+
it (any repository or tarball URL works; `NO_RELEASE` means that one
|
|
83
|
+
publishes none). From a clone instead: `pnpm install` there, then this
|
|
84
|
+
checkout's `pnpm everyapp deploy --json` from that directory (a clone's
|
|
85
|
+
own CLI copy can't deploy; its `pnpm everyapp` is for dev). The key can
|
|
86
|
+
be set before that deploy all the same (`pnpm everyapp configure` in
|
|
87
|
+
OpenSEO's directory reads its `everyapp.config.ts` instead of the
|
|
88
|
+
registry and asks for the Gateway's settings before the first deploy;
|
|
89
|
+
step 5's root list is then the check, and so is
|
|
90
|
+
`pnpm everyapp secret list --json` in that directory).
|
|
91
|
+
2. `pnpm everyapp status --json`: `data.installed` and `data.claimed` are
|
|
92
|
+
true; `data.gatewayUrl` is `https://<gateway host>`; `data.appBaseDomain`
|
|
93
|
+
is `<app base domain>`. The entry in `data.apps` with `"id": "openseo"`
|
|
94
|
+
has `"status": "active"`, `"bound": true` and the same `setup` as 1;
|
|
95
|
+
while a required setting is missing `data.fixes` has `APP_NEEDS_SETUP`
|
|
96
|
+
for it (severity `warning`, headline "openseo needs setup", `run` the
|
|
97
|
+
same command). Not installed, or no owner: [gateway.md](gateway.md)
|
|
98
|
+
first.
|
|
99
|
+
|
|
100
|
+
## Steps
|
|
101
|
+
|
|
102
|
+
One step per message: give the step, wait for the words it ends with ("I'm
|
|
103
|
+
in", "I can see them", "done"), then the next. Not a checklist to send at
|
|
104
|
+
once: steps 1 and 2 catch an unconfirmed email and an IP list before the
|
|
105
|
+
value is set.
|
|
106
|
+
|
|
107
|
+
1. **Person:** Open with the one question: do they already have a
|
|
108
|
+
DataForSEO account, or has OpenSEO run anywhere else? No account: open
|
|
109
|
+
https://app.dataforseo.com/register and create it (goal: a DataForSEO
|
|
110
|
+
account; the API login is whatever the API Access page shows in step 2:
|
|
111
|
+
DataForSEO generates it, on older accounts it is the sign-up email).
|
|
112
|
+
Confirm the email if asked: until it is confirmed the API answers 40104.
|
|
113
|
+
When it worked, the dashboard at app.dataforseo.com opens and shows a $1
|
|
114
|
+
balance. Already registered: sign in at https://app.dataforseo.com. Ran
|
|
115
|
+
elsewhere: an OpenSEO deployed outside Every App held the base64 form of
|
|
116
|
+
the same credential, which works unchanged here (step 4's note), and if
|
|
117
|
+
that deployment whitelisted its server's IP on API Access, the list has
|
|
118
|
+
to be emptied (step 2). Wait for "I'm in".
|
|
119
|
+
2. **Person:** Open https://app.dataforseo.com/api-access (API Access in the
|
|
120
|
+
left menu). It shows the API login, the API password and, for the first 24
|
|
121
|
+
hours after registration, a ready-made Base64 string of `login:password`.
|
|
122
|
+
After 24 hours the password is hidden: click **Send by e-mail** (goal: get
|
|
123
|
+
the password again) and read it from the email. Leave the **IP Access**
|
|
124
|
+
list on this page empty: the Gateway calls from Cloudflare's addresses,
|
|
125
|
+
which change, and one entry makes DataForSEO refuse every other address
|
|
126
|
+
(40207). Keep the page or email open; nothing is typed yet. Wait for "I
|
|
127
|
+
can see them".
|
|
128
|
+
3. **Person:** Optional now, needed before real use: **Add Funds** in the
|
|
129
|
+
Billing section or the top bar (goal: credit for live calls; minimum $50),
|
|
130
|
+
and a daily limit under API Settings, https://app.dataforseo.com/api-settings
|
|
131
|
+
(counted per 24 hours UTC), so a runaway job stops at a number they chose.
|
|
132
|
+
The $1 trial credit covers the free smoke check and about ten keyword
|
|
133
|
+
lookups (at $0.09 a Google Ads request).
|
|
134
|
+
4. **Person:** The README's first way, the person types it. In a second
|
|
135
|
+
terminal in the `every-app` folder: `pnpm everyapp configure openseo`
|
|
136
|
+
(from OpenSEO's directory, `pnpm everyapp configure`, which works before
|
|
137
|
+
its first deploy too), which asks for this key among OpenSEO's other
|
|
138
|
+
unset settings (the OpenRouter key while it is missing: [openrouter.md](openrouter.md)
|
|
139
|
+
says where it comes from; the optional Context key is offered after,
|
|
140
|
+
and Enter skips it), or `pnpm everyapp secret set DATAFORSEO_API_KEY`
|
|
141
|
+
for just this one. At the hidden prompt (`DATAFORSEO_API_KEY`, under
|
|
142
|
+
lines saying what it is for and where it comes from; `secret set` asks
|
|
143
|
+
`Value for DATAFORSEO_API_KEY`), paste the API login, a colon, and the
|
|
144
|
+
API password (`login:password`): the form to recommend, since Verify
|
|
145
|
+
2's check takes it as is. Nothing is echoed. It ends with the setting
|
|
146
|
+
live on the Gateway (`Set DATAFORSEO_API_KEY on the Gateway`; configure
|
|
147
|
+
then says `openseo is ready.` or what it still needs). Wait for "done".
|
|
148
|
+
(The page's Base64 string, or the value an older OpenSEO held, works
|
|
149
|
+
too; Verify 2 then needs its `header =` form.)
|
|
150
|
+
Alternative, README's third way, when they want the smoke check (Verify
|
|
151
|
+
2) as well: in their terminal the hidden read (bash or zsh
|
|
152
|
+
`read -rs DATAFORSEO_API_KEY && export DATAFORSEO_API_KEY`, fish
|
|
153
|
+
`read -s -x DATAFORSEO_API_KEY`), paste `login:password`, Enter, then there
|
|
154
|
+
`pnpm everyapp configure openseo --from-env --json` (every unset setting
|
|
155
|
+
whose variable is exported, at once) or
|
|
156
|
+
`pnpm everyapp secret set DATAFORSEO_API_KEY --from-env DATAFORSEO_API_KEY --json`;
|
|
157
|
+
never a typed `export …=`, which the shell history keeps. No DataForSEO
|
|
158
|
+
CLI or API returns the credential, so the second way does not apply.
|
|
159
|
+
5. **You:** `pnpm everyapp secret list --json`: `DATAFORSEO_API_KEY` is in
|
|
160
|
+
`data.gateway.names`. Then the Verify section.
|
|
161
|
+
|
|
162
|
+
## Verify
|
|
163
|
+
|
|
164
|
+
1. `pnpm everyapp secret list openseo --json`, from anywhere: the
|
|
165
|
+
`DATAFORSEO_API_KEY` entry in `data.app.setup.settings` has `"set": true`
|
|
166
|
+
and has left `data.app.setup.unmet`; `data.app.setup.ready` is true once
|
|
167
|
+
nothing else of OpenSEO's is missing (its OpenRouter key counts too,
|
|
168
|
+
Context doesn't; until then `command` and `data.next` name only what is
|
|
169
|
+
left). In the older fields, its `data.gateway.needed` entry has
|
|
170
|
+
`"set": true` and the name left `data.gateway.missing`. Then
|
|
171
|
+
`pnpm everyapp status --json`: the `openseo` entry's `setup.ready` is
|
|
172
|
+
true and `data.fixes` has no `APP_NEEDS_SETUP` for it, once everything
|
|
173
|
+
else is set. OpenSEO not deployed yet (`APP_NOT_FOUND`): step 5's root
|
|
174
|
+
list is the check until it is.
|
|
175
|
+
2. Smoke check, in the person's terminal (the endpoint is account-wide, so
|
|
176
|
+
the Gateway refuses it for apps; from the terminal it is the right check).
|
|
177
|
+
It needs the variable in that shell, holding the same form they pasted
|
|
178
|
+
in step 4: after the hidden prompt nothing is exported, so either have
|
|
179
|
+
them read it in hidden again for this check (README's third way:
|
|
180
|
+
`read -rs DATAFORSEO_API_KEY && export DATAFORSEO_API_KEY`, paste,
|
|
181
|
+
Enter), or let the app be the check (3). The credential reaches curl
|
|
182
|
+
as a config file on stdin, never on the command line (`printf` is a shell
|
|
183
|
+
builtin, so no process carries it):
|
|
184
|
+
`printf 'user = "%s"\n' "$DATAFORSEO_API_KEY" | curl -s -K - https://api.dataforseo.com/v3/appendix/user_data | jq '{status: .status_code, message: .status_message, balance: .tasks[0].result[0].money.balance}'`
|
|
185
|
+
Expected: `"status": 20000`, `"message": "Ok."`, and the balance in USD
|
|
186
|
+
(1 on a fresh account). The call is free. A wrong credential prints
|
|
187
|
+
`"status": 40100` and `"balance": null`; the Base64 form in `user =`
|
|
188
|
+
prints the same, so when the variable holds Base64 (step 4's note), give
|
|
189
|
+
them this line instead, before reading 40100 as a wrong login or password:
|
|
190
|
+
`printf 'header = "Authorization: Basic %s"\n' "$DATAFORSEO_API_KEY" | curl -s -K - https://api.dataforseo.com/v3/appendix/user_data | jq …`
|
|
191
|
+
Then `unset DATAFORSEO_API_KEY`.
|
|
192
|
+
3. The app: open `https://<app>.<app base domain>` (the id from
|
|
193
|
+
`data.apps`, usually `openseo`), open a project, then **Keyword Research**
|
|
194
|
+
(under Research in the sidebar; the URL ends in `/keywords`), and search
|
|
195
|
+
one keyword. Search volume and difficulty appear; each lookup costs a few
|
|
196
|
+
cents. A browser that lands on the Gateway's setup dialog instead
|
|
197
|
+
(`https://<gateway host>/?setup=openseo`) means a required setting is
|
|
198
|
+
still unset: Verify 1 says which. OpenSEO shows one message for a
|
|
199
|
+
missing key and a rejected one alike: "DataForSEO isn't set up on your
|
|
200
|
+
Every App gateway yet, or rejected the key it holds. An owner or admin
|
|
201
|
+
sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." Tell them apart with
|
|
202
|
+
`pnpm everyapp secret list --json`: `DATAFORSEO_API_KEY` absent from
|
|
203
|
+
`data.gateway.names` means step 4 did not land (behind the Gateway's
|
|
204
|
+
setup gate the app doesn't open in that state, so expect the second);
|
|
205
|
+
present means the value is wrong (Verify 2 says which way). The
|
|
206
|
+
Gateway's log tells them apart too (4).
|
|
207
|
+
4. Logs, if that helps: `cf` has no Worker tail (`cf workers --help` lists
|
|
208
|
+
none). In the Cloudflare dashboard, README's path:
|
|
209
|
+
**Workers & Pages** → `every-app-gateway` → **Observability** (**Logs** on older accounts) → **Live**
|
|
210
|
+
for a stream, or the stored Workers Logs on the same page. Each call that reached
|
|
211
|
+
DataForSEO is one JSON line with `evt: "integration.proxy.call"`,
|
|
212
|
+
`capability: "dataforseo"`, the path, the upstream `status` and
|
|
213
|
+
DataForSEO's `cost`; the keys are indexed, so filter on `evt`. A wrong
|
|
214
|
+
value shows `status: 401` there. A key the Gateway does not hold is
|
|
215
|
+
refused before any call, so no event appears at all;
|
|
216
|
+
`evt: "integration.proxy.upstream_failed"` means DataForSEO could not be
|
|
217
|
+
reached. Without the dashboard, `cf o11y telemetry query --view events --limit 50 --timeframe-from <ms> --timeframe-to <ms> --body '<query JSON>'`
|
|
218
|
+
reads the same stored logs (read-only); the query body that filters on
|
|
219
|
+
`evt` is not worked out yet (unverified).
|
|
220
|
+
|
|
221
|
+
## If it fails
|
|
222
|
+
|
|
223
|
+
| error or symptom | meaning | what to do |
|
|
224
|
+
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
225
|
+
| `SECRET_VALUE_REQUIRED` from `secret set --json` | Nothing was piped and there is no terminal to prompt in | One of the README's three ways: the person's own terminal, or `--from-env` |
|
|
226
|
+
| `SECRET_VALUE_REQUIRED` from `pnpm everyapp configure openseo --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 OpenSEO still needs | The person's own terminal (step 4), `NAME=value` lines on stdin, or the hidden read then its `data.rerun`, `pnpm everyapp configure openseo --from-env` |
|
|
227
|
+
| `SECRET_SEVERAL_NAMES` | `--from-env` or a pipe with more than one name | One name per call |
|
|
228
|
+
| `SECRET_EMPTY` | The `--from-env` variable is empty or not exported in that shell | The hidden read (README's third way) in that same terminal first, then rerun |
|
|
229
|
+
| A browser opening OpenSEO is sent to `https://<gateway host>/?setup=openseo`; any other request gets 503 with the body `{"error":"app_needs_setup","app":"openseo"}` | The Gateway's setup gate: a required setting of OpenSEO is still unset (this key or the OpenRouter one; Context is optional) | `data.app.setup.command`, `pnpm everyapp configure openseo` (step 4), then Verify 1 |
|
|
230
|
+
| `integration_not_configured` (503, header `x-everyapp-error`) | The Gateway holds no `DATAFORSEO_API_KEY`; OpenSEO shows it as "DataForSEO isn't set up on your Every App gateway yet, or rejected the key it holds. An owner or admin sets it with pnpm everyapp secret set DATAFORSEO_API_KEY." (behind the setup gate the app doesn't open in this state) | Steps 4 and 5; check the name's spelling in `data.gateway.names` |
|
|
231
|
+
| `capability_not_requested` (403) | The deployed manifest does not list `capabilities.dataforseo` | OpenSEO's does; a fork must add it to `everyapp.config.ts` and deploy again |
|
|
232
|
+
| `integration_path_not_allowed` (403) | The app called an account-wide path (`appendix/user_data`, `appendix/webhook_resend`, `tasks_ready`, `id_list`, `errors`) | By design: one key serves every app. Check balance and limits from the terminal (Verify 2) |
|
|
233
|
+
| `integration_method_not_allowed` (405) | A method other than GET or POST | DataForSEO reads with GET and POST only |
|
|
234
|
+
| `integration_upstream_failed` (502) | The Gateway could not reach DataForSEO | Retry; a persisting failure is on DataForSEO's side |
|
|
235
|
+
| `status_code` 40100, or HTTP 401, "you are not authorized to access this resource" | Wrong login or password: the sign-in password instead of the API password, a typo, whitespace | Re-read API Access (or Send by e-mail), set it again (step 4); the `user` form needs `login:password`, not base64 |
|
|
236
|
+
| 40104 "please verify your account before using the api" | The sign-up email is not confirmed yet | Open DataForSEO's confirmation email, click its link, rerun Verify 2 |
|
|
237
|
+
| 40200 "payment required", 40210 "insufficient funds" | The balance is too low for the request | Add Funds (minimum $50) |
|
|
238
|
+
| 40203 "the cost limit has been exceeded" | The daily spending limit in API Settings was reached (counted per 24 hours UTC) | Raise it at https://app.dataforseo.com/api-settings, or wait for 00:00 UTC |
|
|
239
|
+
| 40202 "rate-limit per minute has been exceeded", 40209 "too many simultaneous queries" | Over the per-minute or 30-concurrent limit | Retry after a pause; OpenSEO's scheduled checks spread out on their own |
|
|
240
|
+
| 40204 "access denied. visit plans and subscriptions" | Left over from the Backlinks commitment DataForSEO removed on 2026-07-01; a funded pay-as-you-go account should not see it | Contact DataForSEO, https://dataforseo.com/contact |
|
|
241
|
+
| 40207 "access denied. your ip is not whitelisted" | API Access has an IP list; the Gateway's Cloudflare addresses are not on it | The person removes every entry under IP Access on https://app.dataforseo.com/api-access (step 2) |
|
|
242
|
+
| 40201 "unusual activity" | DataForSEO paused the account | The person contacts DataForSEO at https://dataforseo.com/contact (a form; the page says support is available 24/7) |
|
|
243
|
+
| 40400 or 40402 "invalid path" | The endpoint path is wrong | An app bug, not a setup problem: compare with docs.dataforseo.com |
|
|
244
|
+
| `curl` prints nothing, or `jq` reports that the input is not JSON | The variable is empty, or the response is not JSON (a redirect or block page) | `printf … \| curl -s -K - -o /dev/null -w '%{http_code}\n' …` with the same stdin config to see the HTTP status; never print the variable |
|
|
245
|
+
|
|
246
|
+
## Rerun, rotate, remove
|
|
247
|
+
|
|
248
|
+
- **Rerun.** Every step is safe to repeat: the dashboard shows what exists,
|
|
249
|
+
`configure` and `secret set` replace the value in one new Gateway version
|
|
250
|
+
(once OpenSEO is ready, configure's menu changes one setting at a time),
|
|
251
|
+
and the Verify section says where to continue after an interruption.
|
|
252
|
+
- **Rotate.** The person changes the API login in API Access (documented:
|
|
253
|
+
the old `login:password` and its base64 stop working). A new API password
|
|
254
|
+
needs DataForSEO's support (https://dataforseo.com/contact): the help
|
|
255
|
+
center mentions regenerating it from the dashboard but documents no
|
|
256
|
+
control for it, and Send by e-mail resends the same one. Then step 4 again
|
|
257
|
+
with the new value, then Verify. A value that went through the chat is
|
|
258
|
+
compromised: rotate it, then set it one of the three ways.
|
|
259
|
+
- **Remove.** `pnpm everyapp secret unset DATAFORSEO_API_KEY --json`. Without
|
|
260
|
+
`--yes` it exits 1 with `CONFIRMATION_REQUIRED`: that is the ask; rerun
|
|
261
|
+
with `--yes` only once the person has said yes. OpenSEO then needs setup
|
|
262
|
+
again: the Gateway closes it until the key is back (its manifest marks
|
|
263
|
+
`dataforseo` required; `status` says so, with this file's path). Then
|
|
264
|
+
revoke on DataForSEO's side: change the API login (or have
|
|
265
|
+
support issue a new password), or close the account if nothing else uses
|
|
266
|
+
it.
|
|
267
|
+
|
|
268
|
+
## Sources
|
|
269
|
+
|
|
270
|
+
- https://docs.dataforseo.com/v3/auth/ (API login and password, Basic auth, API Access page)
|
|
271
|
+
- https://docs.dataforseo.com/v3/appendix/user_data/ (the free account endpoint; `result` is an array, `money.balance`, `rates.limits`; `backlinks_subscription_expiry_date` deprecated)
|
|
272
|
+
- https://docs.dataforseo.com/v3/appendix/errors/ (status codes 20000 to 50000, 40104, 40207; the 40204 note still names the old Backlinks commitment)
|
|
273
|
+
- https://docs.dataforseo.com/v3/appendix-sandbox/ (sandbox host, free, dummy data, limits)
|
|
274
|
+
- https://dataforseo.com/pricing (pay as you go, $50 minimum, $1 trial credit, funds do not expire)
|
|
275
|
+
- https://dataforseo.com/pricing/keywords-data/google-ads ($0.09 per live task, $0.06 standard, billed per task) and https://dataforseo.com/pricing/dataforseo-labs ($0.012 per task plus $0.00012 per item)
|
|
276
|
+
- https://dataforseo.com/apis/serp-api ("No credit card required", $1 credit, $0.002 per live SERP)
|
|
277
|
+
- https://dataforseo.com/update/pricing-update-in-dataforseo-apis (2026-07-01: the $100 Backlinks and LLM Mentions commitment removed, +20%)
|
|
278
|
+
- https://dataforseo.com/faq (2,000 calls per minute, $50 minimum, trial credit)
|
|
279
|
+
- https://dataforseo.com/help-center/dfs-dashboard-how-to-find-everything (API login is generated and changed in API Access, Send by e-mail, Payments, API Settings; no regenerate control)
|
|
280
|
+
- https://dataforseo.com/help-center/how-to-authenticate-with-dataforseo-google-search-serp-api-api-key-setup-and-code-examples (sign-up link, 24-hour visibility; calls the login the sign-up email)
|
|
281
|
+
- https://dataforseo.com/help-center/how-does-your-free-unlimited-trial-work (trial terms, one account per user)
|
|
282
|
+
- https://dataforseo.com/help-center/whitelist-ips (IP Access on the API Access page, 40207, static IPs only, empty list means no restriction)
|
|
283
|
+
- https://dataforseo.com/help-center/limit-my-payments (API Settings, per API or endpoint, 24 hours UTC, 40203)
|
|
284
|
+
- https://dataforseo.com/contact (form and phone; "support available 24/7"; no support@ address anywhere on it)
|
|
285
|
+
- https://developers.cloudflare.com/workers/observability/logs/real-time-logs/ (Workers & Pages, Overview, Worker, Logs, Live; `npx wrangler tail`) and https://developers.cloudflare.com/workers/observability/logs/workers-logs/ (Observability tab; JSON log keys are indexed as fields)
|
|
286
|
+
- https://app.dataforseo.com/register and https://app.dataforseo.com/api-access (the pages the person opens; both load a signed-in app, so their text was not read)
|
|
287
|
+
- In code: `packages/sdk/src/internal/appSettings.ts` (`AppSetup`: `unmet`, `settings`, `groups`, `command`; `setup/dataforseo.md` from the capability registry, `packages/sdk/src/internal/capabilities.ts`), `packages/cli/src/commands/secret/index.ts` (`secret list <app>` from anywhere, `data.app.setup`, the kept `data.gateway.needed` and `missing`, `APP_NOT_FOUND`), `packages/cli/src/commands/configure/index.ts` (`configure <app>`: the prompts, `NAME=value` lines on stdin, `--from-env`, `SECRET_VALUE_REQUIRED`), `packages/cli/src/lib/app/settings.ts` (the `Guide:` 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 503 `app_needs_setup`); OpenSEO's `everyapp.config.ts` (`dataforseo` required, with its `why`)
|