# Echo Program: API reference > One call, a sealed engine, a verdict. Everything a training team or its coding agent needs to take a forecast, act on it, and report what happened. Service: https://api.echo-program.com. Site: https://echo-program.com. Generated from the same source as https://echo-program.com/docs. ## What the service does Before you spend GPU time curating data toward a target domain, send a sample of the target and a sample of the candidate pool. The engine answers in seconds, on CPU, without touching your model. The corpus never leaves your machines; only samples travel, over HTTPS, processed in memory and never written to disk. | Verdict | Meaning | What you do | | --- | --- | --- | | `SELECT` | Curating toward this target is forecast to help. | Curate. Rank the pool with the scoring table and keep the top. | | `DO-NOT-SELECT` | Curating is forecast to backfire, or the target carries no usable signal. | Train on a random sample of the same size and keep the compute. | | `NO-FORECAST` | Too close to the calibrated boundary to call. | Treat the gain as a coin flip. Add target data and run again. | Every verdict carries a confidence tier (`high`, `medium`, `low`), a scope card, notes, a fingerprint of the exact text it was made on, and a `forecast_id` you use later to report the outcome. Every forecast is timestamped before your first training step. The receipt that ties forecast to outcome is the product. ## Quick start You need Python 3 and your API key from the dashboard. The reference client has no dependencies. ```bash curl -O https://echo-program.com/client/echospace_pilot.py export ECHOSPACE_URL=https://api.echo-program.com export ECHOSPACE_API_KEY=es_pilot_... python3 echospace_pilot.py selftest python3 echospace_pilot.py forecast --target ./target_docs/ --pool ./pool_docs/ --label first-try \ --model-params 124000000 --keep 0.25 --language en --regime pretraining --table --out fc_first.json python3 echospace_pilot.py select-local --forecast fc_first.json --pool ./pool_docs/ --keep 0.25 --out keep.json python3 echospace_pilot.py outcome --forecast fc_xxxxxxxxxxxx --selected 1.91 --baseline 2.05 ``` - `selftest` checks the client's hashing against the service's test vectors. Run it once per machine. - `forecast` draws seeded random samples of whole documents from both folders, sends them, and prints the verdict. With `--table` and a `SELECT` verdict, the saved file also carries the scoring table. - `select-local` ranks your whole pool on your own machine from that table and writes `keep.json`. The pool is never uploaded. - `outcome` reports the held-out result of the run under the forecast id. That is the receipt. `keep.json` holds the indices of the pool documents to keep, in the order the client read them: files in sorted name order, walking folders recursively, then line order inside `.jsonl` files. ## Documents and sizes A document is one string. The client reads a folder recursively: every `.jsonl` line with a `text` field is one document, and any other file is one document. Over raw HTTP you send lists of strings. | Limit | Value | | --- | --- | | Target minimum | about 2,600 words in two or more documents (20 chunks of 128 words) | | Pool minimum | at least 4 times the target, by chunks | | Pools larger than 250,000 chunks | a seeded random sample of whole documents is used; the response says so | | Per call | up to 96 MB compressed on the wire, 512 MB inflated, 400,000 documents | | Per key | 50 engine calls per 24 hours (forecast and select); raised on request | | Client sample defaults | about 5,000,000 pool words and 2,000,000 target words, seed 0 | More target data sharpens the reading. A target that arrives as a single document is accepted but noted; send one entry per document. English, whitespace-delimited text is the validated range; text without word boundaries is flagged in `script_check`. ## Authentication Every call carries the key as a bearer token. Keys are stored hashed on the service and revoked on request; one key per organisation. Send a `User-Agent` naming your tool; a bare default Python agent is rejected at the edge before it reaches the service. ```bash curl -s https://api.echo-program.com/v1/me -H "Authorization: Bearer $ECHOSPACE_API_KEY" -H "User-Agent: my-pipeline/1.0" ``` ```json {"key_id": "key_a6326f70", "org": "Acme Research", "daily_quota": 50, "used_last_24h": 2, "forecasts_total": 7} ``` ## Forecast **POST `/v1/forecast`** — Samples in, verdict out. Counts as one engine call. | Field | Type | Meaning | | --- | --- | --- | | `target` | string[] | sample of target documents, one string each | | `pool` | string[] | sample of candidate-pool documents | | `label` | string, optional | your name for this run, up to 120 characters | | `table` | boolean, default false | on `SELECT`, also return the scoring table | | `context` | object, optional | `model_params` (int), `keep_fraction` (0 to 1), `language` (ISO code), `regime` (`pretraining`, `continued`, `finetuning`). Outside the validated range the confidence drops one tier and the response says why; the verdict does not change. | | `provenance` | object, optional | how the samples were drawn: `client`, `pool_total_documents`, `target_total_documents`, `sample_seed`. Recorded on the receipt, not verified. | Raw HTTP, gzip optional: ```bash curl -s https://api.echo-program.com/v1/forecast \ -H "Authorization: Bearer $ECHOSPACE_API_KEY" -H "Content-Type: application/json" -H "User-Agent: my-pipeline/1.0" \ -d '{"target": ["doc one ...", "doc two ..."], "pool": ["...", "..."], "label": "first-try", "context": {"model_params": 124000000, "keep_fraction": 0.25, "language": "en", "regime": "pretraining"}, "table": true}' ``` Response: ```json { "forecast_id": "fc_7e2cc32e34e0", "verdict": "SELECT", "confidence": "medium", "reason": "...", "unstable": false, "target": {"documents": 6, "chunks": 42, "words": 5376}, "pool": {"documents": 60, "chunks": 281, "words": 35968, "subsampled": false}, "notes": [], "script_check": {"unspaced_fraction": 0.0}, "scope": { "validated": {"model_size": "...", "keep_fraction": "...", "text": "...", "regime": "...", "selector": "..."}, "outside_validated_range": [], "confidence_before_scope": "medium" }, "fingerprint": {"target_sha256": "...", "pool_sha256": "...", "hasher": "es-h1", "engine": "pilot-0.2"}, "provenance": {"client": "my-pipeline/1.0", "pool_total_documents": 812000, "target_total_documents": 3100, "sample_seed": 0}, "engine": "pilot-0.2", "elapsed_s": 0.31, "scoring_table": {"format": "float32-le-base64", "buckets": 16384, "hasher": "es-h1", "sha256": "...", "values": "..."} } ``` - `unstable` true means the two halves of the target disagree on the call; add target data before acting. - `scoring_table` is present only when asked for and the verdict is `SELECT`; otherwise it is `null` with a note. - Keep the response file. `select-local` needs it, and the fingerprint proves later which text the forecast was made on. ## Selection Two ways to turn a `SELECT` into a keep list. The local way is the one to use for any pool you would not want to upload. **POST `/v1/selections`** — Record a keep list you computed locally from the scoring table. The pool never left your machine. Not an engine call. | Field | Type | Meaning | | --- | --- | --- | | `forecast_id` | string | the forecast the table came from | | `keep_fraction` | number | fraction of the pool kept | | `pool_total_documents` | int | documents scored | | `kept` | int | documents kept | | `table_sha256`, `keep_sha256` | string, optional | hashes of the table and of the keep list, for the receipt | | `client` | string, optional | what computed it | Response: ```json {"selection_id": "sel_1db5b923d18a", "forecast_id": "fc_7e2cc32e34e0", "forecast_verdict": "SELECT", "mode": "local", "recorded": {"keep_fraction": 0.25, "pool_total_documents": 812000, "kept": 203000, "table_sha256": "...", "keep_sha256": "..."}} ``` **POST `/v1/select`** — Server-side keep list for a small pool. Takes the same body as a forecast plus `keep_fraction` (default 0.25) and an optional `forecast_id` to reuse a verdict; without one it runs the forecast first. Counts as one engine call. - `SELECT` returns a targeted keep list (`mode: "targeted"`). - `DO-NOT-SELECT` returns a uniform random keep list of the same size (`mode: "random"`). That is the recommended action, not a fallback. - `NO-FORECAST` is refused with 409: the engine will not choose a keep list it cannot stand behind. Response: ```json {"selection_id": "sel_...", "forecast_id": "fc_...", "verdict": "SELECT", "mode": "targeted", "keep_fraction": 0.25, "pool_documents": 4000, "kept": 1000, "keep_indices": [3, 7, 12, ...], "hasher": "es-h1", "engine": "pilot-0.2", "note": "..."} ``` ## The scoring table and the hasher The table is a plain published importance-weighting selector fitted on your own samples: one float per bigram bucket, sent as `float32-le-base64` with its `sha256`. It says nothing about how the verdict was reached. To score a document, hash its word bigrams into buckets, average the table's values over them, and keep the top fraction of the pool by that score. The reference client does exactly this; the service's own selection agrees with it bit for bit. **GET `/v1/hasher`** — The hashing a local scorer must reproduce, with test vectors. Not an engine call. ```json {"hasher": "es-h1", "buckets": 16384, "rule": "tokens = re.findall(r'\\S+', text.lower()); h = zlib.crc32(token.encode('utf-8')) & 0xFFFFFFFF; bucket = (h[i] * 1000003 + h[i+1]) % buckets", "test_vectors": [{"text": "The quick brown fox jumps over the lazy dog.", "word_hashes": [...], "buckets": [...]}, ...]} ``` If you port the scorer into your own pipeline, run the test vectors before every selection. A hasher that drifts produces a keep list that is silently wrong. The version (`es-h1`) is stamped on every forecast, table and selection. ## Outcome **POST `/v1/outcome`** — Report what the training run did. This is the receipt; a forecast without an outcome teaches neither of us anything. Not an engine call. | Field | Type | Meaning | | --- | --- | --- | | `forecast_id` | string | the forecast this run followed | | `acted` | `selected`, `random`, `both`, `none` | which arms you trained | | `metric_name` | string, default `held-out loss` | what the numbers measure | | `selected_value`, `baseline_value` | number, optional | the curated arm and the random arm | | `lower_is_better` | boolean, default true | direction of the metric | | `notes` | string, optional | anything else, up to 2,000 characters | Response: ```json {"outcome_id": "oc_a1f8bc1852a8", "forecast_id": "fc_...", "forecast_verdict": "SELECT", "forecast_agreed_with_outcome": true, "thanks": "recorded; this is the receipt"} ``` Where you can, train both arms: the curated keep list and a random keep list of the same size, same model, same budget, and report the held-out metric of each. One arm is still worth reporting. ## Other calls **GET `/v1/me`** — Your key: organisation, quota, calls used in the last 24 hours, forecasts on record. **GET `/v1/forecasts?limit=50`** — Your forecasts, newest first, up to 500. The same rows appear in the dashboard ledger. **GET `/v1/health`** — Service and engine version. No key needed. The interactive OpenAPI reference is at https://api.echo-program.com/v1/docs and the schema at https://api.echo-program.com/v1/openapi.json. ## Errors | Status | Meaning | | --- | --- | | 401 | missing, invalid or revoked API key | | 404 | `forecast_id` not found for this key | | 409 | `/v1/select` on a `NO-FORECAST` verdict | | 413 | too many documents in one call | | 422 | target or pool too small, empty, or malformed; the message says which | | 429 | daily quota reached | Engine calls run one at a time; a call may wait a few seconds behind another. Retry on network failure is safe: a forecast that did not return was not recorded. ## Data handling - Sent: samples only, over HTTPS, held in memory for the call, never written to disk, never reused. - Kept: document and word counts, the fingerprint of the sample, the verdict, timestamps, the sampling provenance, and any outcome you report. Never your text. Never the scoring table. - Aggregated, anonymised forecast and outcome statistics may improve calibration and appear in our publications. Never partner-identifiable, never text. ## For coding agents ### Setup ```bash curl -O https://echo-program.com/client/echospace_pilot.py # Python 3, no dependencies export ECHOSPACE_URL=https://api.echo-program.com export ECHOSPACE_API_KEY=es_pilot_... # from the member's dashboard; never commit it python3 echospace_pilot.py selftest # once per machine ``` ### Workflow - Put target documents in one folder and candidate-pool documents in another. One file is one document; a `.jsonl` file is one document per line under `text`. - Forecast: `python3 echospace_pilot.py forecast --target T/ --pool P/ --label NAME --model-params N --keep 0.25 --language en --regime pretraining --table --out fc_NAME.json`. Always pass `--model-params`, `--keep`, `--language`, `--regime`: the scope card depends on them. - Read `verdict` and `confidence` from the saved JSON. Act by the verdict table below. Never override a `DO-NOT-SELECT` with a targeted keep list. - On `SELECT`: `python3 echospace_pilot.py select-local --forecast fc_NAME.json --pool P/ --keep 0.25 --out keep_NAME.json`. `keep_indices` are pool document indices in the client's read order (sorted file names, recursive, then `.jsonl` line order). Build the training set from them. - On `DO-NOT-SELECT`: train on a uniform random sample of the pool of the same size. That is the recommendation, not a fallback. - On `NO-FORECAST`: do not curate on this evidence. Add target data and forecast again. - After training, report: `python3 echospace_pilot.py outcome --forecast fc_... --selected --baseline --metric "held-out loss" --acted both`. Train both arms when the budget allows. | Verdict | Action | | --- | --- | | `SELECT` | curate with the scoring table via `select-local`; keep the top fraction | | `DO-NOT-SELECT` | random sample of the same size; keep the compute | | `NO-FORECAST` | no curation decision; add target data, rerun | ### Rules - Never upload a large pool: `forecast` sends samples and `select-local` scores the pool locally. Use `select` (server-side keep list) only for small pools. - Never port the hasher without running the `/v1/hasher` test vectors first; a drifted hasher yields a silently wrong keep list. - Minimums: target about 2,600 words in two or more documents; pool at least four times the target. A 422 tells you which is short. - Quota: 50 engine calls per key per 24 hours (`forecast` and `select`). `selections`, `outcome`, `me`, `hasher` are free. - Keep every `fc_*.json` and `keep_*.json` with the training run; the fingerprint ties the forecast to the exact text. - Confidence `low` or a note about the validated range means the job is outside what the service has been checked on; say so in your report to the human. - Outputs are forecasts, not guarantees. The training decision belongs to the human; present the verdict, the confidence, and the notes. ### Reference - Full API reference: https://echo-program.com/llms-full.txt (also https://echo-program.com/docs) - OpenAPI: https://api.echo-program.com/v1/openapi.json - Reference client: https://echo-program.com/client/echospace_pilot.py