Use Y2 from the Command Line
Install the native Y2 harness, save an API key, and call Y2 from a terminal
Use Y2 Information Dominance for interactive agent work and one-off prompts. Use curl for reproducible API checks, pagination, exports, and JSON selection.
Install the native harness
The installer supports macOS and Linux on x86-64 and arm64. It downloads the matching release,
verifies its SHA-256 checksum, and installs y2 under ~/.y2/bin by default.
The initial macOS CLI archives are SHA-256 verified native binaries, but they are not Developer ID signed or Apple notarized. The signed and notarized Apple release is intentionally deferred.
export PATH="$HOME/.y2/bin:$PATH" && curl -fsSL https://y2.dev/harness/install.sh | shThis one command makes y2 available in the current terminal and adds the same PATH entry to your
detected shell profile for future terminals. Pass --no-modify-path to leave shell profiles
unchanged. Verify the binary:
export PATH="$HOME/.y2/bin:$PATH"
y2 --versionPin an exact release when you need a reproducible install:
export PATH="$HOME/.y2/bin:$PATH" && curl -fsSL https://y2.dev/harness/install.sh | sh -s -- v0.0.7Upgrade an installed harness on the stable channel:
y2 upgradeSave a Y2 API key
Create a key with the agent:y2 scope. New API keys require a Pro or Elite workspace plan.
y2 auth opens Y2 API Keys in your browser, prints the URL as a fallback, and securely prompts
for the key:
y2 authThe harness stores the key in its supported local credential backend. Run y2 setup when you
already have a key and want to enter it without opening the browser. For ephemeral shells and CI,
set the key in the environment instead:
export Y2_API_KEY="y2_..."Keep the key out of shell history, logs, prompts, tool arguments, and browser bundles.
Run Agent Y2
Start an interactive session from the project you want the harness to use as its workspace:
cd your-project
y2Run one noninteractive prompt with y2 ask:
y2 ask "Summarize the changes in this repository"The CLI sends its local tool schemas through Agent Y2's isolated harness mode. It discovers
applicable AGENTS.md files from the workspace root down to each target file, makes skills and
subagents available to the model, and keeps file and command execution on your machine behind the
configured permission policy. Y2's API returns tool calls; it does not execute the CLI's local tools
on the server.
Agent Y2 is the default route. Inspect the active configuration and local preflight without printing the key:
y2 status --json
y2 doctorTo use another OpenAI-compatible endpoint directly, provide its base URL, key, and model:
export OPENAI_BASE_URL="https://your-provider.example/v1"
export OPENAI_API_KEY="your-provider-api-key"
export Y2_MODEL="your-model-id"
y2OPENAI_BASE_URL may include /chat/completions; otherwise y2 appends it. See the
Agent Y2 API guide for the default endpoint and streaming contract.
Use curl directly
You need curl and, for JSON processing, jq:
curl --version
jq --version
export Y2_API_KEY="y2_..."New API keys require Pro or Elite. The key must carry the scope required by each operation.
Shell history, debug tracing, process listings, and CI output can expose secrets. Keep the key in
the environment, quote variable expansions, and do not use set -x in credentialed scripts.
Create a reusable GET helper
#!/usr/bin/env bash
set -euo pipefail
: "${Y2_API_KEY:?Set Y2_API_KEY before running this script}"
y2_get() {
local path="$1"
curl --fail-with-body --silent --show-error \
"https://api.y2.dev${path}" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
}
y2_get "/api/v1/news/feeds" | jq '.meta'The helper accepts origin-relative API paths such as /api/v1/news and
/api/v2/incidents. That also makes it safe to pass a pagination link returned by Y2.
Discover News topics
List all current topic IDs and names:
y2_get "/api/v1/news/feeds" |
jq -r '.data[] | [.id, .name, .groupLabel] | @tsv'Then request a bounded page:
y2_get "/api/v1/news?topics=markets,macro&limit=10" |
jq -r '.data[] | [
.publishedAt,
(.sentiment.label // "unknown"),
.title,
(.links.canonical // "")
] | @tsv'Current News rows use title, summary, and nested sentiment fields. Use the feed directory
instead of assuming an older crypto-only topic list.
Inspect profile and subscription IDs
GET /profiles returns paired resources. Print both IDs before scripting updates:
y2_get "/api/v1/profiles" |
jq -r '.data[] | [
.profile.id,
.subscription.id,
.profile.name,
.subscription.delivery.method
] | @tsv'Use prf_... for profile configuration and report filtering. Use sub_... when changing delivery.
Create a profile idempotently
profile_id=$(
curl --fail-with-body --silent --show-error \
"https://api.y2.dev/api/v1/profiles" \
--request POST \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: supplier-risk-2026-07-21" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting strategic semiconductor suppliers.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00"
}' |
jq -r '.data.id'
)
printf '%s\n' "$profile_id"The response ID is the profile's canonical prf_... ID. Reuse the same idempotency key only with
the same request body.
Paginate every report for a profile
Y2 returns an opaque continuation link. Follow it unchanged:
#!/usr/bin/env bash
set -euo pipefail
: "${Y2_API_KEY:?Set Y2_API_KEY}"
: "${PROFILE_ID:?Set PROFILE_ID to a prf_ ID}"
next="/api/v1/reports?profileId=$PROFILE_ID&limit=20"
while [ -n "$next" ]; do
page=$(
curl --fail-with-body --silent --show-error \
"https://api.y2.dev${next}" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
)
jq -r '.data[] | [.id, .publishedAt, (.summary // "")] | @tsv' <<<"$page"
next=$(jq -r '.links.next // empty' <<<"$page")
doneDo not decode or edit the cursor. It is bound to the original filters and ordering.
Export one report as Markdown
: "${REPORT_ID:?Set REPORT_ID to an rpt_ ID}"
curl --fail-with-body --silent --show-error \
"https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/markdown" \
--output "$REPORT_ID.md"The file contains the report body directly. It is not a JSON envelope.
Process NDJSON one row at a time
News and several list endpoints can return newline-delimited JSON:
curl --fail-with-body --silent --show-error --get \
"https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/x-ndjson" \
--data-urlencode "topics=cybersecurity" \
--data-urlencode "limit=50" |
while IFS= read -r row; do
jq -r '[.publishedAt, .title] | @tsv' <<<"$row"
doneFor multi-page ingestion, also capture X-Y2-Next-Cursor from the response headers and send it as
the next request's cursor value.
Capture status, headers, and errors
Use temporary files when a script needs to branch on HTTP status while retaining Problem Details:
body_file=$(mktemp)
headers_file=$(mktemp)
trap 'rm -f "$body_file" "$headers_file"' EXIT
status=$(
curl --silent --show-error \
--output "$body_file" \
--dump-header "$headers_file" \
--write-out '%{http_code}' \
"https://api.y2.dev/api/v1/reports/not-a-report-id" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
)
if [ "$status" -ge 400 ]; then
jq '{status, code, detail, requestId}' "$body_file" >&2
grep -i '^Retry-After:' "$headers_file" >&2 || true
exit 1
fiLog requestId, not credentials. Retry safe reads or idempotent writes only, and follow
Retry-After on 429.
Call Intel v2
Intel paths use /api/v2 directly:
y2_get "/api/v2/incidents?limit=20" |
jq -r '.data[] | [.id, .severity, .title] | @tsv'Do not place /api/v1 before an Intel v2 path.
Know when curl is not enough
curl can inspect the initial 402 and PAYMENT-REQUIRED header, but it does not create an
EIP-3009 signature by itself. Use an x402 v2-capable wallet client, then retry with the generated
PAYMENT-SIGNATURE.