# Y2 Affiliate Program Terms (2026-09-24-v5) (/docs/affiliate-program-terms-2026-09-24-v5) # Y2 Affiliate Program Terms **Version:** `2026-09-24-v5` **Effective time:** The fixed UTC time shown in Settings → Referrals. The time is recorded in Y2's production policy configuration when this version takes effect. These Affiliate Program Terms supplement the Y2 Terms of Service between YEETUM, LLC, doing business as Y2 (“Y2”), and the participating Y2 account holder (“Promoter”). They govern referral commissions under this version. A payment's verified first collection time determines its commission policy. Payments before the effective time retain their applicable prior terms: the accepted `2026-09-17-v4` native policy covered Lite and Pro, and protected imported provider liabilities retain their original terms. This version does not recalculate, erase, or newly commission earlier payments. ## 1. Joining and account standing Participation is voluntary. You must affirmatively accept the current terms before your assigned referral code and link become active. To earn commission on a payment under this version, you must have accepted version `2026-09-24-v5` no later than that payment's verified collection time. You must have a verified email, own a billing workspace with a paid, active, current Lite, Pro, or Elite subscription, maintain your account in good standing, and satisfy Y2's identity, tax, and payout-provider requirements. Membership in someone else's paid workspace does not qualify you. Your own Lite subscription is sufficient; the Pro/Elite restriction concerns the referred customer's purchase. Y2 records your acceptance, attribution, payments, adjustments, and payout decisions. Joining, sharing a link, or a displayed estimate does not guarantee an approved or payable commission. ## 2. Qualifying referred customers A referral must be a bona fide new customer who uses your assigned link or accepted code before their first paid Y2 subscription payment. The customer must be a separate person and account; self-referrals, duplicate or fabricated accounts, manipulated attribution, and customers who paid before the saved claim do not qualify. Optional browser link capture can last up to 90 days. A valid saved claim cannot be replaced and expires after 90 days for its first qualifying payment. For payments first collected at or after the effective time, only a successful, non-zero Pro or Elite subscription purchase can create the first qualifying conversion and commission. A Free or Lite purchase, signup, trial, fully discounted invoice, or uncollected payment earns nothing under this version. A referred customer's paid Lite activity remains visible in the referral dashboard. If that same attributed subscription upgrades to Pro or Elite and its first qualifying payment is collected within the original 90-day claim window, that upgrade may be the first qualifying conversion. Earlier Lite invoices remain noncommissionable under this version. Renewals of the qualifying attributed subscription may earn recurring commissions while all conditions remain satisfied; they do not add another distinct customer conversion. Attribution does not follow a customer to a different subscription automatically. ## 3. Commission calculation and corrections The commission is 15% of eligible Pro or Elite subscription revenue actually collected by Y2, after excluding taxes, discounts and credits and subtracting actual attributable incoming Stripe payment-processing fees. Commission rounds down to the smallest unit of the payment currency. For example, a $50.00 eligible payment with no tax and a hypothetical $2.00 processing fee produces $48.00 net revenue and a $7.20 commission. The example is not a promise of a Stripe fee. If fee, attribution, identity, payment, or historical evidence is incomplete, Y2 may hold the calculation for review. Refunds, credits, chargebacks, lost disputes, fee corrections, and other reversals may reduce or remove commission, including after a payout through an offset against future earnings. Y2 will keep an auditable record of the reason and adjustment. An existing amount is not recomputed solely because these terms changed. ## 4. Review and payout Commission remains under review for at least 60 days after the qualifying payment and may remain held while evidence or a dispute is unresolved. The initial payout route supports verified US recipients and USD. A request requires at least $100 in eligible commission after adjustments and before the payout deduction, a current eligible Y2 account, a ready Stripe recipient, complete historical reconciliation, and a verified current cost tariff. Payout availability, supported countries and currencies may change prospectively after notice. For each USD payout batch under this version, Y2 deducts **3% of the batch's gross eligible commission plus $0.50**. The percentage component rounds up to the nearest cent. This is the entire affiliate payout deduction: actual Stripe sending costs are not added on top, and the deduction does not change when those costs are reported or corrected later. For example, a $100.00 gross commission batch has a $3.50 deduction and a $96.50 transfer to Stripe. Y2 bears any difference between this deduction and its actual sending costs. Incoming Stripe payment-processing fees were already subtracted when commission was calculated and are not charged again at payout. Payout batches earned under prior terms retain their own cost rules. Y2 withholds this fixed deduction before sending the transfer. The dashboard distinguishes gross commission, deduction, transfer to Stripe, and confirmed bank delivery. A Stripe transfer does not itself prove bank delivery. Y2 does not debit your bank account for a later change in its Stripe sending costs. ## 5. Responsible promotion Describe Y2 accurately. Clearly and conspicuously disclose your financial connection with each endorsement, near the recommendation or referral link; for example, “I may earn a commission if you buy Y2 through my link.” A disclosure only in a profile, footer, or separate terms page is insufficient. Follow applicable advertising, endorsement, privacy, anti-spam, consumer-protection, and platform rules. Do not impersonate Y2, send spam, use deceptive redirects, stuff cookies, manipulate attribution, bid on Y2 trademarks without permission, offer an unapproved incentive, or make false, unsubstantiated, or guaranteed claims. Y2 may review, hold, reverse, suspend, or end participation for suspected fraud, abuse, nonpayment, misleading promotion, or legal or provider risk. Y2 will review available evidence before a final decision and will preserve records of existing liabilities and lawful offsets. You control how you promote Y2, bear your own expenses, and may not bind Y2. Participation is not employment, an investment, a franchise, or a guarantee of income. ## 6. Taxes, records, changes, and disputes You are responsible for taxes on amounts received and for accurate payout and tax information. Y2 or its provider may request verification and withhold payout where required by law or while required information is incomplete. Y2 may retain financial, attribution, acceptance, and review records as described in its privacy notice and applicable law. Y2 may change eligible plans, rates, costs, review periods, thresholds, or these terms prospectively. Material changes require publication in the product and acceptance where applicable. Changes do not retroactively alter a payment's governing policy or approved historical liability. Termination or account deletion stops future participation but does not silently erase valid balances, adjustments, tax obligations, audit rights, or required financial records. The general dispute, liability, and governing-law provisions of the Y2 Terms of Service apply unless a program-specific term here controls. By selecting the acceptance checkbox and confirming in Settings → Referrals, you agree to version `2026-09-24-v5`. The English text controls. A Spanish-language summary is available in Settings → Referrals; it does not replace these complete terms. --- # Join and Manage the Affiliate Program (/docs/affiliate-program) This guide shows you how to join the Y2 Affiliate Program and manage your referral link from [**Settings → Referrals**](/app/settings/referrals). Review your current eligibility, accept the program terms, and manage your referral activity. Compare the Y2 plans that can qualify an account for the program. Read the [complete v5 Affiliate Program Terms](/docs/affiliate-program-terms-2026-09-24-v5) and the UTC effective time in **Settings → Referrals** before accepting or sharing a link. The Spanish text in Settings is a summary; the complete English terms control. ## Before you start Sign in with the account that owns the paid billing workspace you want to use. Referral settings will show any requirement that still needs attention, including: - A verified account email. - Ownership of a qualifying paid workspace. - A current subscription payment for that workspace. Your own current paid Lite, Pro, or Elite subscription can qualify you to participate. For referred customer payments from the Pro/Elite policy's displayed UTC effective time, only a Pro or Elite purchase can earn commission. A referred Lite purchase remains visible as a referral but earns nothing under the new policy. A later Pro or Elite upgrade can become its first qualifying conversion if its payment falls within 90 days of the saved claim. Admin or member access to another customer's paid workspace does not qualify the account. Use [Account settings](/app/settings/account) to resolve email requirements and [Billing settings](/app/settings/billing) to resolve plan or payment requirements. ## Join the program ### Open referral settings Go to [**Settings → Referrals**](/app/settings/referrals). The program card shows whether your account is ready to enroll or which requirement needs attention. ### Review the current terms Expand **Review the affiliate program terms**. Review the version, commission rules, payout requirements, and responsible-promotion rules shown in the app. ### Accept and join Select the agreement checkbox, then choose **Join and create my referral link**. Y2 records the terms version you accepted before starting link creation. ### Copy the activated link Wait for your complete referral URL to appear, then choose **Copy link**. ## Manage your referral activity Return to [**Settings → Referrals**](/app/settings/referrals) whenever you need to: - Copy your current referral link. - Review saved referrals, including those waiting on a qualifying purchase, conversions, active qualifying subscriptions, and expired or rejected claims. - Check payout eligibility requirements. - Review verified payments, commission calculations, and payout statuses. For each USD payout batch under the current Pro/Elite terms, Y2 deducts 3% of the batch's gross commission plus $0.50, rounding the percentage up to the cent. A $100.00 commission batch therefore sends $96.50 to Stripe. This is the full affiliate payout deduction; later changes to Stripe's actual sending costs do not change it. Earlier earnings keep their accepted payout rules. Always share the complete link displayed in the app. Do not reconstruct or shorten its referral token. Disclose that you may earn a commission clearly and close to every endorsement or referral link, such as “I may earn a commission if you buy Y2 through my link.” ## Keep your account eligible Use the **Payout eligibility** card in referral settings as your checklist. Keep every displayed requirement satisfied while participating and when commissions are reviewed or paid. If the card shows an action is required, resolve that item before relying on new referral activity. ## Troubleshoot enrollment or synchronization Verify the email in **Settings → Account**, then reopen **Settings → Referrals**. Confirm that this account owns the billing workspace, then restore the qualifying plan or subscription in **Settings → Billing**. Wait briefly and reopen referral settings. If Stripe onboarding displays **Retry**, choose it after the issue has been resolved. Follow the message shown in the app. If it requests support review, contact [support@y2.dev](mailto:support@y2.dev) and include the displayed error text. --- # Agent Y2 API (/docs/api/agent-y2) Agent Y2 exposes the same preconfigured intelligence agent used by the Y2 copilot through two API-key-authenticated streaming endpoints. It uses the key's workspace, plan entitlements, shared Agent Y2 credit balance, persisted thread history, and allowed Y2 tools. API callers cannot select an arbitrary model, enable onboarding mode, or attach files in v1. The `model` value on the OpenAI-compatible route is a Y2 routing alias, not model selection. Requests without client tools use the fixed Agent Y2 Copilot instructions. A non-empty `tools` array selects the isolated client-harness contract described below. ## Choose an endpoint Both endpoints use `https://api.y2.dev/api/v1`. | Endpoint | Stream format | Choose it when | | --- | --- | --- | | `POST /agent-y2/chat/stream` | Vercel AI SDK UI message stream | Your client understands the native AI SDK protocol | | `POST /chat/completions` | OpenAI-style `chat.completion.chunk` server-sent events | Your client already consumes streaming chat completions | ```mermaid flowchart LR client["API client"] --> auth["API key + agent:y2"] auth --> limits["Key, account, and organization-credit checks"] limits --> context["Workspace thread + entitlements"] context --> agent["Fixed Agent Y2 model and tools"] agent --> format{"Response format"} format --> native["AI SDK UI stream"] format --> openai["OpenAI-compatible SSE"] ``` The routes share the same Agent Y2-specific rate-limit pool. Switching formats does not create a second quota. ## Create a scoped key ### Open API Keys In the workspace that should own the threads and tool actions, open **Settings → Developers → API Keys**. ### Grant `agent:y2` Create a dedicated key with the `agent:y2` scope. New API-key creation requires Pro or Elite. Existing Lite keys remain usable only with the scopes already granted to them. ### Store the key on a trusted server Send it as `Authorization: Bearer $Y2_API_KEY`. Do not put an Agent Y2 key in browser bundles, prompts, metadata, logs, or public repositories. Agent Y2 does not support x402. A Y2 `402` with code `INSUFFICIENT_CREDITS` means the workspace's shared Agent Y2 balance is exhausted; it is not a payment challenge. ## Send a native request ```bash title="Terminal" curl -N -i "https://api.y2.dev/api/v1/agent-y2/chat/stream" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "user", "parts": [ { "type": "text", "text": "What changed in cyber risk this week?" } ] } ], "metadata": { "source": "customer_crm", "externalThreadId": "case-123" } }' ``` The response is an AI SDK UI message stream. API responses omit internal reasoning parts even though the in-app stream can include them. Read the `X-Thread-Id` response header and store it. Continue the conversation by sending that ID in the next native request body: ```json title="request.json" { "threadId": "j57...", "messages": [ { "role": "user", "parts": [{ "type": "text", "text": "Which entities should I watch next?" }] } ] } ``` ## Send an OpenAI-compatible request ```bash title="Terminal" curl -N -i "https://api.y2.dev/api/v1/chat/completions" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "y2-agent", "stream": true, "messages": [ { "role": "user", "content": "Summarize the latest Y2 platform capabilities." } ] }' ``` The compatibility route requires `stream: true`. `model` is optional; when supplied, it must be `y2-agent` or `agent-y2`. It emits a role chunk, text or tool-call delta chunks, a final chunk with `finish_reason: "stop"` or `"tool_calls"`, and `data: [DONE]`. To continue a conversation, pass `threadId` in the JSON body or send the prior ID in an `X-Thread-Id` request header. Supplying a non-empty `tools` array switches this request to client-harness mode. Y2 sends the schemas to the model and streams function calls back, but it never executes those functions. Your client remains responsible for permission checks, local execution, and returning matching tool results. Agent Y2's server-side intelligence tools are not exposed in this mode. ## Use client-side tools Client-harness mode preserves system and developer instructions, assistant tool calls, and matching tool results. This is the mode used by the native Y2 CLI for repository instructions, local file operations, commands, skills, and subagents. ```bash title="Terminal" curl -N -i "https://api.y2.dev/api/v1/chat/completions" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "y2-agent", "stream": true, "messages": [ { "role": "system", "content": "Inspect the workspace before editing." }, { "role": "user", "content": "Read README.md." } ], "tools": [ { "type": "function", "function": { "name": "read_file", "description": "Read a workspace file", "parameters": { "type": "object", "properties": { "path": { "type": "string" } }, "required": ["path"] } } } ], "tool_choice": "auto" }' ``` The response header `X-Y2-Agent-Mode: harness` confirms that the client-tool contract was selected. Tool definitions are limited to 128 functions. Names, descriptions, schemas, transcript length, and tool arguments are bounded; malformed or unmatched tool history returns `400` before model execution. ## Understand thread context | Request state | Context used by Agent Y2 | | --- | --- | | No `threadId` | Creates an API-source thread and uses non-empty user and assistant text from the supplied transcript | | Existing `threadId` | Verifies the same user, API source, and workspace, then loads up to 40 persisted messages from that thread | | Latest user message | Saved to the thread and used as the new prompt | | System, developer, or tool transcript entries without `tools` | Accepted for compatibility but excluded from the fixed Copilot prompt | | Non-empty `tools` array | Uses client-harness mode with supplied instructions and complete matching tool history | A thread created in the app cannot be continued through the API. A workspace-scoped API key also cannot continue a thread from another workspace. The server returns `400` for an invalid ID shape, `404` for a missing thread, and `403` when the thread exists outside the caller's allowed source, user, or workspace scope. ## Available tools The exact tool set is assembled for every request from current workspace entitlements. | Tool category | Availability | | --- | --- | | Y2 documentation search | Always registered | | Public briefing search and follow | Registered for standard Agent Y2 sessions | | News search | Registered for standard sessions | | Web search | Requires the workspace web-search entitlement | | Profile creation and editing | Requires remaining custom-profile capability and metering context | | OSINT search | Requires OSINT entitlement | | Y2 Global Knowledge retrieval | Requires Chat entitlement; searches only authorized shared and key-workspace corpora | | Cyber, FININT, markets, entity, incident, and investigation tools | Added according to the related intelligence entitlements | | Image generation | Requires image-generation entitlement and a compatible selected agent path | Tool availability does not guarantee that the agent will call a tool. Tool actions still enforce the underlying workspace permissions and resource limits. This table applies to Copilot mode; client-harness mode advertises only the caller's function schemas and executes none of them on Y2. Agent Y2 uses its existing `agent:y2` scope when it invokes Y2 Global Knowledge. Do not add `intel:knowledge` to an Agent Y2 key unless the same integration also calls the direct retrieval endpoint. Direct retrieval and agent execution are separate least-privilege surfaces. ## Metadata behavior Both routes accept a strict metadata object: ```json { "metadata": { "source": "ops_console", "externalUserId": "analyst-42", "externalThreadId": "case-123" } } ``` | Field | Maximum | Current behavior | | --- | --- | --- | | `source` | 128 characters | Accepted for compatibility; the thread source is still recorded as `api` | | `externalUserId` | 256 characters | Accepted but not currently persisted to the thread | | `externalThreadId` | 256 characters | Stored when Y2 creates a new thread; it does not replace the Y2 `threadId` | Unknown metadata properties fail strict validation. Keep your own correlation log and never put secrets or full customer records in metadata. ## Provider privacy controls The current Agent Y2 executor selects chat models marked as Zero Data Retention capable and sends OpenRouter provider options with `zdr: true` and `data_collection: "deny"`. These controls govern the external model request. Y2 still stores API-source threads, messages, request attribution, tool results, and usage records under Y2's product data lifecycle. See the [Privacy Policy](/privacy) for the customer-facing data terms. ## Rate limits and response headers Agent Y2 applies these limits in addition to the API key's normal plan limits: | Limit scope | Per minute | Per day | | --- | ---: | ---: | | API key | 5 | 100 | | User or workspace aggregate | 10 | 250 | Inspect `Retry-After`, the normal `X-RateLimit-*` headers, and the `X-Y2-Agent-RateLimit-*` headers. Successful streams also return `X-Thread-Id`, `X-Y2-Agent: y2`, and `X-Y2-Agent-Mode: copilot` or `X-Y2-Agent-Mode: harness`. ## Error guide | Status | Common cause | Response | | --- | --- | --- | | `400` | Invalid JSON, missing latest user text, unsupported alias, invalid tool schema or history, invalid thread ID, or native `modelId`, `isOnboarding`, or `attachments` | Correct the request; do not retry unchanged | | `401` | Missing, malformed, revoked, or invalid API key | Replace or rotate the credential | | `402` | Subscription, workspace Agent Y2 credit, or upstream provider credit exhaustion | Check the workspace plan and Agent Y2 credit balance; do not start x402 handling | | `403` | Missing scope, no chat entitlement, blocked access, or unauthorized thread | Check key scope, workspace, plan, and thread origin | | `404` | Supplied thread no longer exists | Start a new thread or correct the stored ID | | `429` | Normal API or shared Agent Y2 limit exceeded | Back off for `Retry-After` seconds | | `500` | Stream preparation or provider execution failed | Retry with backoff and retain `X-Request-Id` for support | ## Related reference Verify the exact request schemas, response headers, and generated examples Understand key format, scopes, tenant resolution, and plan limits Connect an MCP-compatible agent to Y2's exposed tools --- # AI Quickstart (/docs/api/agentic-dx) Use this page before asking a coding agent to build against Y2. It gives the agent stable discovery URLs, separates the v1 and v2 API roots, identifies the required scopes, and keeps credentials out of the prompt. The button copies a short bootstrap prompt that tells the agent to read this page and the MCP guide. It intentionally links to current documentation instead of embedding a second copy of plan limits that can drift. ## Agent build loop ### Discover the documentation Start with `https://y2.dev/llms.txt`. It is the compact index generated from the documentation tree. Load `llms-full.txt` only when the task needs broad cross-product context. ### Read the relevant guide and operation Use the manual guide for intent and workflow, then use the generated endpoint reference or raw OpenAPI operation for the exact path, method, parameters, scopes, schemas, x402 setting, and response headers. ### Resolve authentication and tenancy Create the API key in the workspace that should own the request. Grant only the documented scopes. Let Y2 derive user and workspace authorization from the key; never treat a caller- supplied user or workspace ID as proof of access. ### Implement from the contract Keep `Y2_API_KEY` in a server or local agent environment. Generate types from OpenAPI when useful, but preserve Y2 response headers and structured errors in the integration layer. ### Verify one real request Run a bounded curl request or use the API Workbench. Confirm the status, response shape, `X-Request-Id`, API version, pagination, and rate-limit headers before expanding the workflow. ## Canonical context URLs | Resource | URL | Use | | --- | --- | --- | | Documentation index | `https://y2.dev/llms.txt` | Discover pages without loading the full corpus | | Full documentation | `https://y2.dev/llms-full.txt` | Broad context for cross-cutting work | | Individual raw page | `https://y2.dev/docs/{path}.mdx` | Load one page as Markdown | | API guide | `https://y2.dev/docs/api` | Understand surfaces, auth choices, and workflow boundaries | | Generated reference | `https://y2.dev/docs/api/reference` | Inspect operation-specific schemas and examples | | OpenAPI 3.1 document | `https://y2.dev/api/openapi.yaml` | Generate clients and validate exact contracts | | Authentication guide | `https://y2.dev/docs/api/authentication` | Choose key scopes and understand tenant resolution | | Agent Y2 guide | `https://y2.dev/docs/api/agent-y2` | Integrate native or OpenAI-compatible agent streams | | MCP guide | `https://y2.dev/docs/api/mcp` | Configure the separately distributed local MCP server | `llms.txt`, `llms-full.txt`, and raw `.mdx` pages are generated from `content/docs` during the site build and served by the local Vite documentation middleware during development. Manual guides explain goals and safe usage. If a hand-written example conflicts with a generated operation, use `public/api/openapi.yaml` and the generated endpoint page as the field-level source of truth, then report the stale guide. ## Base URLs | Surface | Base URL | Examples | | --- | --- | --- | | Core v1 | `https://api.y2.dev/api/v1` | Reports, profiles, news, OSINT, webhooks, subscriptions, Agent Y2 | | Intelligence v2 | `https://api.y2.dev/api/v2` | Changes, incidents, entities, graphs, markets, FININT, signals, cyber | The OpenAPI document declares an operation-level `https://api.y2.dev` server for v2 paths because those paths already include `/api/v2`. Do not combine the v1 base with a v2 operation path. ## API-key plan and scope map This table describes the new API-key path. An endpoint that explicitly supports x402 can also be called without an API key by completing that endpoint's payment flow. | Surface | New key minimum | Required scope | | --- | --- | --- | | Reports | Pro | `reports:read` | | Report audio | Pro | `reports:audio` | | Profiles | Pro | `profiles:read` or `profiles:write` by operation | | News | Pro | `news:read` | | OSINT v1 | Pro | `osint:read` | | FININT and markets v2 | Pro | `intel:finint` | | Cyber v2 | Pro | `intel:cyber` | | Explorer entities, incidents, and graphs | Pro | `intel:explorer` | | Webhook management API | Pro | `webhooks:manage` | | Agent Y2 stream | Pro | `agent:y2` | | MCP docs and OpenAPI resources | None | None | | MCP data or Agent Y2 tools | Pro for a new key | Scope used by each underlying API operation | Lite includes in-app FININT, Cyber, Explorer, full Situation Room access, and webhook delivery, but cannot create new API keys. A Lite workspace with existing keys retains those credentials and their historical limit of 10 requests per minute and 500 per day. This grace does not grant new scopes or new key creation. | Plan | New keys | Default per-key request limits | | --- | ---: | ---: | | Free | 0 | No key access | | Lite | 0 | Existing keys only: 10/minute and 500/day | | Pro | 10 | 30/minute and 5,000/day | | Elite | 100 | 300/minute and 50,000/day | Elite workspaces include five seats, with larger organizations available by arrangement. Workspace members share tenant resources and account-level rate-limit aggregates; creating more keys does not multiply the workspace aggregate quota. ## Give an agent safe instructions Use this compact task template after the bootstrap prompt has loaded the docs: ```text title="Agent task template" Implement [specific Y2 workflow] in this codebase. Before editing: 1. Read the relevant Y2 manual guide. 2. Read the exact operation in https://y2.dev/api/openapi.yaml. 3. Confirm the base URL, required scope, pagination, x402 support, and error responses. Constraints: - Read Y2_API_KEY from the server or MCP client environment only. - Never put the key in source, browser code, prompts, tool arguments, or logs. - Let the API key determine tenant access. - Use the narrowest scopes and bounded page sizes. - Handle 401, 402, 403, 404, 409/412 where documented, 429, and 5xx. - Preserve X-Request-Id and Retry-After for diagnostics. Verify with one bounded request and report the observed status and response shape. ``` ## MCP boundary The platform repository defines the HTTP APIs, OpenAPI contract, docs, billing rules, and Agent Y2 executor. `@y2-intel/mcp` is documented as a separate stdio package and its implementation is not present in this repository. Follow the [MCP guide](/docs/api/mcp) for the intended configuration. Before automating against a specific package release, inspect its actual `tools/list`, `resources/list`, and `prompts/list` responses instead of assuming every documented capability exists in the installed version. When configuring a local MCP client, pass `Y2_API_KEY` through the client environment. Grant `agent:y2` only when the agent should be able to use entitled account actions; documentation and OpenAPI discovery do not require that scope. ## Smoke tests ```bash title="Terminal" curl -sS -D - "https://api.y2.dev/api/v1/reports?limit=1" \ -H "Authorization: Bearer $Y2_API_KEY" ``` ```bash title="Terminal" curl -sS -D - "https://api.y2.dev/api/v2/finint?limit=1" \ -H "Authorization: Bearer $Y2_API_KEY" ``` Use a key that has the matching read scope. `-D -` prints the response headers so the agent can verify request IDs, API version, and remaining quota instead of checking only the JSON body. ## Related guides Choose the right API surface and authentication path Understand key format, scopes, IP rules, limits, and workspace tenancy Generate clients and maintain the contract Run a live request and copy environment-based snippets --- # Authentication (/docs/api/authentication) Y2 authenticates most API requests with a workspace-bound bearer key. Endpoints that explicitly support [x402](/docs/api/x402) also offer a separate paid request path when no bearer key is sent. ## Authentication flow ```mermaid flowchart LR request["HTTP request"] --> bearer{"Authorization starts\nwith Bearer?"} bearer -->|Yes| key["Validate hash, state, plan, IP"] key --> scope["Check operation scope"] scope --> limits["Increment key + tenant limits"] limits --> handler["Run in key workspace"] bearer -->|No| payment{"Endpoint supports x402?"} payment -->|Yes| x402["Payment challenge or signed retry"] payment -->|No| unauthorized["401"] ``` ## Plan requirements | Plan | New keys | Default limit per key | Notes | | --- | ---: | ---: | --- | | Free | 0 | No key access | Use in-app features or supported x402 endpoints | | Lite | 0 | Existing keys: 10/minute and 500/day | Grandfathered keys only; historical active-key capacity is 2 | | Pro | 10 | 30/minute and 5,000/day | New API-key access | | Elite | 100 | 300/minute and 50,000/day | New API-key access plus team governance | Existing keys that were already eligible under the Lite grace policy remain visible, usable, rotatable, and revocable. A later downgrade from Pro or Elite marks keys plan-ineligible; it does not create new Lite grace access. Rate limits apply per key and again to the account aggregate: the workspace for workspace-bound keys, or the user for a legacy unscoped key. Creating more keys does not multiply the tenant quota. ## Create a key ### Select the correct workspace Use the workspace switcher first. Y2 binds the new key to the active workspace, and later API requests resolve their tenant from that stored binding. ### Open API Keys Go to [**Developers → API Keys**](/app/developers/api-keys). Creating a key requires an owner or admin role in the workspace and a Pro or Elite plan. ### Name and scope the key Use a unique operational name such as `report-exporter-prod`. Select only the scopes required by that integration. At least one scope is required. ### Add exact IPs when useful Optionally add the stable public egress IP addresses that are allowed to use the key. Confirm the deployed service's actual egress address before enabling this restriction. ### Copy the secret once Y2 displays the full key only in the creation result. Store it in a secret manager or protected environment variable before closing the dialog. ## Key format and storage Y2 generates 32 random bytes and encodes them as 64 hexadecimal characters after the `y2_` prefix: ```text y2_{64 lowercase hexadecimal characters} ``` Only the SHA-256 hash and a short display prefix are stored. The dashboard cannot retrieve the plaintext later. Regeneration creates new secret material and immediately invalidates the prior value. ## Send the key ```bash title="Terminal" curl -sS -D - "https://api.y2.dev/api/v1/reports?limit=1" \ -H "Authorization: Bearer $Y2_API_KEY" ``` The authentication scheme is the case-sensitive `Bearer ` prefix followed by the key. Keep the credential server-side; never place it in a browser bundle, query string, prompt, tool argument, or log message. [API Workbench](/app/developers/api-workbench) keeps the pasted value in React component state and sends it only when you run a request. Generated curl, TypeScript, and Python snippets continue to reference `Y2_API_KEY`. Treat the browser session and screen as sensitive while the key is present. ## Workspace scope | Boundary | Current behavior | | --- | --- | | Resource access | Profiles, subscriptions, reports, Projects, Automations, webhooks, threads, public IDs, and usage are resolved in the key's stored workspace where the operation is tenant-scoped | | Key management | Workspace owners and admins can list and manage workspace keys; other roles receive no key list | | Aggregate limits | All keys in a workspace contribute to the same plan-level minute and daily pool | | Workspace switching | Switching the application workspace does not move an existing key; it changes where a newly created key is bound | Do not accept a user-provided `workspaceId` as authorization. If an operation accepts a resource ID, the backend still verifies that the resource belongs to the tenant resolved from the key. ## Scopes | Scope | Grants access to | | --- | --- | | `reports:read` | Report lists, individual reports, text, signals, and report graphs where specified | | `reports:audio` | Report audio and audio-text operations | | `profiles:read` | Listing profile subscription records in the key workspace | | `profiles:write` | Creating and changing owned profiles, including destructive profile deletion | | `projects:read` | Listing and inspecting owned Projects in the key workspace | | `projects:write` | Creating, changing, archiving, restoring, and pinning owned Projects | | `automations:read` | Listing owned Automation definitions and run history | | `automations:write` | Creating, changing, archiving, and manually running owned Automations | | `news:read` | News items, recaps, and feed discovery | | `webhooks:manage` | Webhook configuration and subscription-delivery webhook operations | | `osint:read` | v1 Situation Room endpoints and the v2 change feed | | `intel:finint` | v2 FININT, markets, shared incidents, and shared signals | | `intel:cyber` | v2 cyber graph, CVEs, threat actors, shared incidents, and shared signals | | `intel:explorer` | v2 entities, entity graphs, shared incidents, and shared signals | | `intel:knowledge` | Direct bounded retrieval across authorized Y2 Global Knowledge corpora | | `agent:y2` | Native and OpenAI-compatible Agent Y2 streams with entitled account tools | | `ledger:read` | Workspace ontology ledger folds, record exports, seal verification, and STIX 2.1 exports | | `ledger:write` | Opening ledger subjects and appending designators, observations, and claims | An array of required Intel scopes in the generated reference means **any one** of the listed scopes can authorize that shared endpoint. Always check the exact operation's `x-required-scopes` value. Plan entitlements are enforced again during key validation. Storing a scope on a key does not keep that capability active after the workspace loses the required plan feature. `intel:knowledge` and `agent:y2` are intentionally separate. Direct `POST /api/v2/intel/knowledge/retrieve` calls require `intel:knowledge`. Agent Y2 routes continue to require only `agent:y2`; an entitled agent can invoke Global Knowledge through the shared internal tool. Existing keys are never expanded automatically—edit or rotate a key deliberately when an integration needs `intel:knowledge`, `projects:*`, `automations:*`, or `ledger:*`. Workspace research endpoints require a workspace-bound key: company financial history (`intel:finint`), cyber-market fusion (`intel:explorer`), and the ontology ledger (`ledger:*`). A personal key receives `403`, and x402 payment is not accepted for them. `profiles:write`, `projects:write`, `automations:write`, `ledger:write`, `webhooks:manage`, and `agent:y2` are not read-only. Isolate them from reporting keys and grant them only to integrations designed to perform the related actions. ## IP allowlisting The dashboard's IP restriction currently performs an exact string comparison against the client IP resolved from `CF-Connecting-IP`, the first `X-Forwarded-For` value, or `X-Real-IP`. - Enter individual IPv4 or IPv6 addresses only. - CIDR ranges are not parsed by the current middleware. - For hosted jobs, allow the service's public egress IP, not a private container address. - Removing every entry disables the key-specific IP restriction. - A resolved address not present in the list returns `403` with `IP_NOT_ALLOWED`. Core API responses use permissive CORS headers, and stored `allowedOrigins` metadata is not currently enforced by request authentication. Use exact IP restrictions where appropriate and keep keys out of client-side applications. ## x402 selection On an x402-enabled operation, the request path is selected by the header prefix: | Request | Result | | --- | --- | | Valid `Authorization: Bearer ...` | Validate key, scope, IP, and key/account limits | | Invalid, expired, revoked, or unauthorized bearer key | Return the API-key `401` or `403`; do not fall back to payment | | No header beginning with `Bearer ` | Enter x402 on a supported endpoint, or return an authentication error when x402 is unavailable | | Signed x402 retry | Verify replay protection, wallet and endpoint limits, and settlement before returning data | Do not send a placeholder bearer header when you intend to use x402. See the [x402 guide](/docs/api/x402) and the operation's generated reference for price, network, and payment headers. ## Rate-limit headers Y2 uses fixed UTC minute and day windows. Authenticated responses expose both pools: | Header family | Meaning | | --- | --- | | `X-RateLimit-Limit-Minute`, `X-RateLimit-Remaining-Minute` | Per-key minute pool | | `X-RateLimit-Limit-Day`, `X-RateLimit-Remaining-Day` | Per-key daily pool | | `X-RateLimit-Limit-User-*`, `X-RateLimit-Remaining-User-*` | Account aggregate; legacy header name, workspace-backed for workspace keys | | `X-RateLimit-Reset-Minute`, `X-RateLimit-Reset-Day` | Unix reset timestamps when included | | `Retry-After` | Seconds to wait after a `429` | Agent Y2 adds a third, endpoint-specific header family. See [Agent Y2 rate limits](/docs/api/agent-y2#rate-limits-and-response-headers). ## Rotate, revoke, or delete Replaces the stored hash, resets key usage metadata, reactivates the record, and displays a new secret once. The old secret stops working immediately. Deactivates the key without deleting its record. Repeating the action is safe. Permanently removes the key record. Use this only after dependent integrations have been migrated. For routine rotation, deploy support for the replacement secret before invalidating the old key if your architecture allows parallel credentials. The dashboard's **Regenerate** action does not provide an overlap window for the same key record. ## Errors and lockout Y2 API errors use RFC 9457-style problem details with an actionable `resolution` hint: ```json { "type": "https://api.y2.dev/problems/insufficient-scope", "title": "Forbidden", "status": 403, "detail": "Insufficient permissions. Requires reports:read scope", "instance": "urn:y2:request:req_...", "code": "INSUFFICIENT_SCOPE", "requestId": "req_...", "resolution": "Verify that the API key belongs to the intended workspace and grants the required scope." } ``` | Status | Common authentication cause | | --- | --- | | `401` | Missing header, unknown key, revoked key, or expired key | | `403` | Plan no longer eligible, missing operation scope, or IP not allowed | | `429` | Per-key/account quota exceeded or source IP temporarily locked after repeated invalid credentials | Five invalid or unknown key attempts from one resolved IP within 15 minutes trigger an authentication lockout for that rolling window. Revoked, expired, and plan-ineligible known keys do not count as brute-force failures. If a valid key suddenly receives a pre-validation `429`, stop retrying and wait for the lockout window to clear. ## Related reference Check the exact scope, x402 setting, schemas, and response headers Compare key counts, plan features, and workspace limits Run a bounded authenticated request without copying the secret into snippets --- # Automations API (/docs/api/automations) Automations run bounded Agent Y2 instructions on a schedule or after a selected profile report. Definitions and runs remain owner-private inside the API key workspace. ## Scopes and availability | Operation | Scope | | --- | --- | | List definitions, inspect one, or read run history | `automations:read` | | Create, update, archive, or request a manual run | `automations:write` | Automations and both API scopes require Pro or Elite. The same workspace definition limits, entitlement checks, overlap policy, and shared Agent Y2 credit admission used by the application also apply to API requests. ## Create a scheduled Automation ```bash curl -sS "https://api.y2.dev/api/v1/automations" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: automation-daily-watch-2026-07" \ -d '{ "name": "Daily supply chain watch", "instructions": "Summarize material changes and cite the strongest evidence.", "projectId": "prj_0123456789abcdef01234567", "trigger": { "kind": "schedule", "frequency": "daily", "timeOfDay": "13:00", "timezone": "America/Chicago" }, "notificationPolicy": "failures_only", "status": "active" }' ``` For a report-completion trigger, use: ```json { "kind": "profile_report_completed", "profileId": "prf_0123456789abcdef01234567" } ``` ## Inspect and update Use `GET /api/v1/automations`, `GET /api/v1/automations/{automationId}`, and `GET /api/v1/automations/{automationId}/runs`. List responses are bounded to 50 rows per request. Definition and run-history collections return the standard opaque `nextCursor` and `links.next` fields for complete traversal. `PATCH /api/v1/automations/{automationId}` accepts definition fields or a status-only lifecycle change. An archived Automation cannot be edited or resumed. Send the latest `ETag` in `If-Match` when coordinating multiple writers. ## Request a manual run ```bash curl -sS -X POST "https://api.y2.dev/api/v1/automations/$AUTOMATION_ID/runs" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Idempotency-Key: manual-run-2026-07-24T1500Z" ``` The endpoint returns `202 Accepted`. The idempotency key is required and prevents duplicate admission for retries. Poll the run-history endpoint for queued, running, terminal, usage, and billing state. Exact paths, parameters, request bodies, responses, and schemas Triggers, plan limits, run states, billing, and live admission checks --- # Y2 Global Knowledge (/docs/api/global-knowledge) Y2 Global Knowledge finds relevant report sections, signal assessments, report graph context, and supported public documents discovered by Y2 research with one hybrid semantic and lexical retrieval request. It is designed for discovery and cross-report synthesis; use the precise Reports or Intel endpoints when your workflow needs a complete current resource. Native embedding access has been approved for the private beta. Production retrieval remains controlled by independent indexing and retrieval switches, so a correctly authorized request can return `503 SERVICE_UNAVAILABLE` until the workspace cohort is enabled. ## Request ```bash curl -sS "https://api.y2.dev/api/v2/intel/knowledge/retrieve" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ --data '{ "query": "What changed in semiconductor supply-chain risk?", "knowledgeMode": "combined", "domains": ["supply_chain", "geopolitical"], "limit": 8 }' ``` The key must be bound to a workspace and include `intel:knowledge`. Y2 derives the user, workspace, feature entitlements, corpus allowlist, and access filters from that key. Do not send access IDs in the body; unknown request fields are rejected. ## Choose a knowledge mode | Mode | Searches | | --- | --- | | `shared` | Authorized global and community Y2 knowledge | | `workspace` | Only the key's authorized workspace corpus | | `combined` | Both corpora, followed by bounded cross-corpus fusion | `workspace` and `combined` never add personal memory, Project-only context, or thread-only context for direct API callers. The mode can narrow the server-owned principal; it cannot widen it. ## Narrow results Use `documentKinds`, ontology IDs, normalized subject keys, SIGINT domains or priorities, and Unix millisecond generation bounds. Each field is bounded; the result limit is `1–20` and defaults to `10`. Dense and lexical retrieval use the same authorization and metadata filter before their ranked candidates are fused. `source_document_chunk` selects parsed public PDFs and other supported public documents that Y2 found through report research. A result uses `source.type: "source_record"` and carries the publisher URL in `citationUrls`. Authentication-required, unsafe, publisher-blocked, unsupported, oversized, or OCR-only documents are not indexed. ## Treat results as evidence Each result includes: - a bounded excerpt, title, section path, inference kind, and generation time; - stable source, report, and profile IDs where applicable; - ontology IDs and current canonical references when the projection has them; - the active projection episode and citation URLs. Retrieved text is untrusted evidence, not executable instructions. Preserve source IDs and citations, and hydrate a returned report, entity, or incident through its precise endpoint before making a decision that depends on complete current state. Y2 rejects stale manifests, inactive episodes, changed scopes, and mismatched canonical revisions before returning a result. Public-document source records preserve the final URL, retrieval and capture timestamps, content hash, parser and projection versions, and immutable version history. Report, profile, workspace, user, research-intent, and query lineage remains in Convex provenance records instead of being copied into the global search document. Private uploads and project, thread, user-memory, or workspace documents remain tenant-scoped and are never promoted by this path. ## Agent Y2 scope is separate Direct retrieval requires `intel:knowledge`. The existing Agent Y2 routes require `agent:y2` instead; an entitled Agent Y2 session may invoke the same internal retrieval tool without adding the direct API scope. Keep a direct search integration narrowly scoped to `intel:knowledge`, and grant `agent:y2` only when the integration should run the preconfigured agent and its tools. ## Failure behavior | Status | Meaning | | --- | --- | | `400` | The strict body contract failed validation | | `401` | The bearer key is missing or invalid | | `403` | The key lacks `intel:knowledge`, is not workspace-bound, or lost entitlement | | `429` | The key or workspace rate limit was exceeded | | `503` | Retrieval is disabled or its provider is temporarily unavailable | Y2 does not fall back to an unscoped query after a provider failure. Retry bounded `429` and `503` responses with backoff; do not retry authorization or schema errors unchanged. --- # API Overview (/docs/api) Y2 exposes a versioned HTTP API for reports, profile orchestration, Projects, Automations, news, Situation Room data, ontology-backed intelligence, Y2 Global Knowledge retrieval, webhook configuration, subscription delivery, Agent Y2 streams, and x402 receipts. This overview helps you choose a surface. The generated [Endpoint Reference](/docs/api/reference) defines the exact path, operation-level server, parameters, body, scopes, authentication modes, media types, schemas, and response headers. ## Request path ```mermaid flowchart LR client["Integration"] --> surface{"API surface"} surface --> v1["Core v1"] surface --> v2["Intel v2"] v1 --> auth{"Bearer key or x402"} v2 --> auth auth --> tenant["Scope + tenant + limits"] tenant --> operation["OpenAPI operation"] operation --> response["Resource, collection, representation, or stream"] ``` ## Base URLs | Surface | Base URL | Contains | | --- | --- | --- | | Core v1 | `https://api.y2.dev/api/v1` | Agent Y2, reports, profiles, Projects, Automations, news, OSINT, webhooks, subscriptions, receipts | | Intelligence v2 | `https://api.y2.dev/api/v2` | Change feed, incidents, entities, graphs, markets, FININT, signals, cyber, fusion, ontology ledger | The OpenAPI document defines core operation paths relative to `/api/v1`. Its v2 operation paths already contain `/api/v2` and override the server to `https://api.y2.dev`. A generator that honors operation-level servers produces the correct URL; do not concatenate a v2 path onto the v1 base. ## Choose authentication | Path | Use when | Identity and limits | | --- | --- | --- | | Bearer API key | The integration should access a Y2 workspace repeatedly | Key scopes, key IP restriction, per-key quota, and workspace/user aggregate quota | | x402 | A listed read operation should be paid per request without provisioning a key | Payment signature, replay protection, wallet quota, endpoint ceiling, and settlement | | Public receipt lookup | A client needs the status of an x402 settlement nonce | No API key; receipt lookup only | New keys require Pro or Elite. Lite cannot create keys, though keys already covered by the Lite grace policy continue at 10 requests per minute and 500 per day. x402 support is independent and is declared one operation at a time. See [Authentication](/docs/api/authentication) for workspace binding and scopes, or [x402 Payments](/docs/api/x402) for the payment handshake. ## Choose a surface Stream the preconfigured workspace-aware agent through native or OpenAI-compatible SSE List and retrieve generated reports, text, signals, graphs, audio, and audio text List subscription records and create, replace, patch, or delete owned profiles Create and manage owner-private intelligence workspaces Manage durable Agent Y2 definitions, manual runs, and run history Read direct-source news items, AI recaps, and the current feed catalog Read v1 Situation Room events, map data, country resources, transport, markets, and source health Consume ontology-backed changes, incidents, entities, graphs, FININT, signals, and cyber data Append evidenced claims, fold them at a record time and a valid time, and export STIX 2.1 Retrieve bounded, authorized evidence across Y2 reports and signal assessments Create, inspect, update, test, and delete webhook configurations Change the supported delivery configuration for a profile subscription Look up an x402 receipt by nonce ## Core v1 route map | Area | Routes | Bearer scope | x402 | | --- | --- | --- | --- | | Agent Y2 | `POST /agent-y2/chat/stream`, `POST /chat/completions` | `agent:y2` | No | | Reports | `GET /reports`, `GET /reports/{reportId}` and report subresources | `reports:read`; audio uses `reports:audio` | Yes where declared | | Profiles | `GET`, `POST /profiles`; `PUT`, `PATCH`, `DELETE /profiles/{profileId}` | Read or write scope by operation | No | | Projects | `GET`, `POST /projects`; `GET`, `PATCH /projects/{projectId}` | `projects:read` or `projects:write` | No | | Automations | `GET`, `POST /automations`; `GET`, `PATCH /automations/{automationId}`; `GET`, `POST /automations/{automationId}/runs` | `automations:read` or `automations:write` | No | | News | `GET /news`, `/news/recaps`, `/news/feeds` | `news:read` | Yes | | OSINT | `GET /osint/*` including country resources | `osint:read` | Yes | | Webhooks | `/webhooks`, `/webhooks/{webhookId}`, `/test` | `webhooks:manage` | No | | Subscription delivery | `PATCH /subscriptions/{subscriptionId}/delivery` | `webhooks:manage` | No | | x402 receipt | `GET /x402/receipts/{nonce}` | None | Public lookup | The reports list is workspace-contextual for bearer callers. In particular, `GET /profiles` lists profile subscription records available to the key workspace; it is not a public community-catalog search endpoint. ## Intel v2 route map | Route | Bearer scope | | --- | --- | | `GET /changes` | `osint:read` | | `GET /incidents`, `GET /incidents/{incidentId}` | Any of `intel:explorer`, `intel:finint`, or `intel:cyber` | | `GET /entities`, `/entities/{entityId}`, `/entities/{entityId}/graph` | `intel:explorer` | | `GET /entities/{entityId}/financial-observations` | `intel:finint`, workspace-bound key | | `GET /entities/{entityId}/fusion` | `intel:explorer`, workspace-bound key | | `GET`, `POST /ledger/subjects`; `GET /ledger/subjects/{subjectId}`, `/stix` | `ledger:read`; opening uses `ledger:write` | | `GET`, `POST /ledger/records`; `GET /ledger/verify` | `ledger:read`; appending uses `ledger:write` | | `GET /markets`, `/finint` | `intel:finint` | | `GET /signals` | Any of `intel:explorer`, `intel:finint`, or `intel:cyber` | | `GET /cyber/graph`, `/cyber/cves`, `/cyber/actors` | `intel:cyber` | | `POST /intel/knowledge/retrieve` | `intel:knowledge` | These paths are relative to `https://api.y2.dev/api/v2` in the table above. Global Knowledge, company financial history, cyber-market fusion, and the ontology ledger are API-key-only because their authorization requires a Y2 workspace principal. The other v2 operations also declare x402 metadata in OpenAPI. ## Success representations Do not assume every successful endpoint returns the same JSON envelope. | Contract kind | Typical response | | --- | --- | | Collection | `{ "data": [...], "meta": {...}, "links": {...} }` or operation-specific pagination fields | | Resource | `{ "data": {...} }` plus operation-specific fields and headers | | Representation | Plain text, audio metadata, or an optional redirect as documented | | Agent stream | `text/event-stream` in AI SDK UI or OpenAI-compatible chunk format | | Delete | `204 No Content` for the documented destructive operations | Collection cursors are opaque. Persist the returned next cursor or watermark and send it back unchanged; do not parse it or construct one from record IDs. ## Errors JSON failures use `application/problem+json` with stable top-level fields and an actionable `resolution` hint: ```json { "type": "https://api.y2.dev/problems/unauthorized", "title": "Unauthorized", "status": 401, "detail": "Invalid API key", "instance": "urn:y2:request:req_...", "code": "INVALID_API_KEY", "requestId": "req_...", "resolution": "Send a valid Y2 API key in the Authorization: Bearer header." } ``` | Status | Common meaning | | --- | --- | | `400` | Invalid query, ID, cursor, JSON, or request body | | `401` | Missing or invalid bearer authentication | | `402` | x402 challenge or rejection; on Agent Y2, plan or credit exhaustion instead | | `403` | Missing scope, ineligible plan, IP restriction, or tenant authorization failure | | `404` | Resource or thread not found in the allowed scope | | `409` | Idempotency or resource-state conflict | | `412` | `If-Match` does not match the current representation | | `415` / `422` | Unsupported representation or operation-specific semantic failure | | `429` | Key, account, endpoint, wallet, or abuse-control limit exceeded | | `500` / `503` | Internal failure, unavailable dependency, or unavailable data path | Retain `X-Request-Id` and the problem `code` in integration logs. Follow `Retry-After` only when the response indicates a retryable limit or availability failure. ## Version and diagnostic headers | Header | Meaning | | --- | --- | | `X-Y2-API-Version` | `1.0` for v1 operations and `2.0` for v2 operations | | `X-Y2-Schema-Version` | Shared response-schema version, currently `1.2` | | `X-Request-Id` | Per-request diagnostic identifier | | `ETag` | Strong validator on operations that support conditional writes | | `Location` | Canonical URL after resource creation | | `Idempotency-Key` | Echoed retry key on operations that support idempotent creation | | `X-Y2-Next-Cursor`, `X-Y2-As-Of`, `X-Y2-Watermark` | Collection and change-feed checkpoints where applicable | ## Explore and verify ### Read the task guide Start with [Integration Recipes](/docs/api/integration-recipes) or the manual guide for the product area. ### Open the generated operation Verify method, server, scope, x402 metadata, parameters, request body, and responses. ### Run a bounded request Use [API Workbench](/app/developers/api-workbench) or curl with the smallest useful `limit`. ### Capture the contract evidence Check the body and diagnostic headers before implementing pagination, retries, or mutations. ## Integration guides Ground a coding agent in Y2 docs and OpenAPI before it writes code Download the contract and generate a client Build and run requests from the current OpenAPI document Configure the separately distributed local MCP server --- # Integration Recipes (/docs/api/integration-recipes) Use these recipes when you already have a Y2 API key and need a production-shaped integration. They use the canonical schemas in the [OpenAPI document](/api/openapi.yaml). Keep `Y2_API_KEY` and webhook secrets in server-side environment variables. | Goal | Start with | | --- | --- | | Ground an agent answer | Report metadata and Markdown | | Import data into a map or spatial analysis tool | GeoJSON FeatureCollection | | Synchronize an analytics warehouse | Paginated Intel v2 JSON | | Keep another system current | Change feed and webhooks | Persist public IDs such as `rpt_...` and `prf_...`. Legacy Convex document IDs are not a stable integration contract. ## Ground an agent with a report and its sources Fetch the report's structured source metadata and canonical Markdown representation in parallel. Retry only rate-limit and server failures, and retain `requestId` when logging a Problem Details response. ```ts const authorization = `Bearer ${process.env.Y2_API_KEY}`; const reportId = "rpt_0123456789abcdef01234567"; const reportUrl = `https://api.y2.dev/api/v1/reports/${reportId}`; async function requireSuccess(response: Response) { if (response.ok) return response; const problem = await response.json(); const error = new Error( `${problem.code}: ${problem.detail} (request ${problem.requestId})`, ); Object.assign(error, { retryable: problem.status === 429 || problem.status >= 500, problem, }); throw error; } const [metadataResponse, markdownResponse] = await Promise.all([ fetch(`${reportUrl}?include=sources&view=agent`, { headers: { Authorization: authorization }, }), fetch(reportUrl, { headers: { Authorization: authorization, Accept: "text/markdown" }, }), ]); const [metadata, markdown] = await Promise.all([ requireSuccess(metadataResponse).then((response) => response.json()), requireSuccess(markdownResponse).then((response) => response.text()), ]); answerWithCitations( markdown, metadata.data.sources.map((source: { url: string }) => source.url), ); ``` The repository checks its generated TypeScript definitions with: ```bash bun run generate:api-types bun run check:api-types ``` External TypeScript projects can generate their own types from `https://y2.dev/api/openapi.yaml` with an OpenAPI 3.1-compatible generator. ## Import regional intelligence into a GIS Request GeoJSON from the regional OSINT endpoint. Coordinates use WGS 84 longitude-first order: `[longitude, latitude]`. ```python import os import requests response = requests.get( "https://api.y2.dev/api/v1/osint/regional", headers={ "Authorization": f"Bearer {os.environ['Y2_API_KEY']}", "Accept": "application/geo+json", }, params={ "bbox": "-122.5,32.5,-96.8,49.2", "datetime": "2026-07-01T00:00:00Z/2026-07-21T23:59:59Z", "limit": 200, }, timeout=30, ) response.raise_for_status() feature_collection = response.json() assert feature_collection["type"] == "FeatureCollection" assert all( feature["type"] == "Feature" for feature in feature_collection["features"] ) next_page = next( ( link["href"] for link in feature_collection.get("links", []) if link.get("rel") == "next" ), None, ) ``` The bounding box filter excludes records without matching coordinates. Open the returned FeatureCollection directly in QGIS or ArcGIS, or load it with GeoPandas. Follow the item in `links` whose `rel` is `next` to retrieve another page. ## Sync financial intelligence into a warehouse The semantic `/api/v2/finint` collection returns JSON with pagination in `meta.page` and `links.next`. Merge on the canonical public `id`, and treat the returned next link as an opaque, filter-bound continuation. ```python import os from urllib.parse import urljoin import requests origin = "https://api.y2.dev" url = f"{origin}/api/v2/finint?limit=500" headers = {"Authorization": f"Bearer {os.environ['Y2_API_KEY']}"} while url: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() page = response.json() warehouse.merge("y2_finint_facts", page["data"], key="id") next_link = page["links"]["next"] url = urljoin(origin, next_link) if next_link else None ``` `/api/v2/finint` does not offer NDJSON. If a row-oriented stream is required, use `/api/v1/osint/finint?format=ndjson`; that endpoint returns at most 100 rows per request and exposes continuation in `X-Y2-Next-Cursor`. Model semantic fields as facts and dimensions. Do not make deprecated epoch aliases or raw provider payloads part of the warehouse contract. ## Build a durable change pipeline Use webhooks for low-latency report notifications and `/api/v2/changes` to detect missed or updated resources. A bearer API key receives global intelligence changes plus changes belonging to its own user or workspace; it does not receive another tenant's records. ```ts async function ingestChanges(savedWatermark?: string) { const first = new URL("https://api.y2.dev/api/v2/changes"); first.searchParams.set("limit", "500"); if (savedWatermark) first.searchParams.set("watermark", savedWatermark); let next: URL | null = first; while (next) { const response = await requireSuccess( await fetch(next, { headers: { Authorization: `Bearer ${process.env.Y2_API_KEY}` }, }), ); const page = await response.json(); // Implement this as one transaction: apply every resource change or // tombstone, then persist the page watermark as the final write. await applyPageAtomically(page.data, page.meta.watermark); next = page.links.next ? new URL(page.links.next, "https://api.y2.dev") : null; } } ``` The watermark is an exclusive checkpoint. Reusing the last committed value after a failure is safe; saving it before resource writes commit can lose changes. A tombstone instructs the consumer to remove or retire the named resource. For webhook requests, verify HMAC-SHA256 over the exact raw request bytes before parsing JSON. Deduplicate on the CloudEvent `id` (also sent as `Idempotency-Key` and `X-Y2-Event-Id`) before performing side effects, and acknowledge a duplicate with a `2xx` response. Y2 does not run an automatic retry loop for failed webhook deliveries. Use the change feed as the repair path; five consecutive delivery failures disable the webhook configuration. --- # Query the Intel API (v2) (/docs/api/intel) Intel v2 turns normalized observations into stable incidents, entities, relationships, markets, and decision signals. Use it when your integration needs to follow identity and relationships instead of processing a raw event feed. The base URL is: ```text https://api.y2.dev/api/v2 ``` Explorer investigation boards are an in-app feature. The public Intel API exposes the ontology resources that can inform an investigation, but it does not expose investigation-board CRUD endpoints. ## Choose a collection | Goal | Endpoint | Primary scope | | --- | --- | --- | | Find normalized incidents | `GET /incidents` | Depends on `category` | | Search ontology entities | `GET /entities` | `intel:explorer` | | Read a single entity | `GET /entities/{entityId}` | `intel:explorer` | | Traverse relationships | `GET /entities/{entityId}/graph` | `intel:explorer` | | Read workspace company financial history | `GET /entities/{entityId}/financial-observations` | `intel:finint` (workspace key) | | Fuse cyber and market exposure for a company | `GET /entities/{entityId}/fusion` | `intel:explorer` (workspace key) | | Record and read evidenced claims | `/ledger/*` ([Ontology Ledger](/docs/api/ontology-ledger)) | `ledger:read`, `ledger:write` | | Read prediction markets | `GET /markets` | `intel:finint` | | Read financial indicators | `GET /finint` | `intel:finint` | | Find extracted decision signals | `GET /signals` | Depends on `domain` | | Traverse a cyber-focused graph | `GET /cyber/graph` | `intel:cyber` | | Search CVEs | `GET /cyber/cves` | `intel:cyber` | | Search threat actors | `GET /cyber/actors` | `intel:cyber` | `GET /api/v2/changes` is the durable change feed for public intelligence resources. It uses `osint:read`, not an Intel scope; see [Integration Recipes](/docs/api/integration-recipes) for a checkpointed ingestion pattern. ## List incidents Start with the narrowest category, lifecycle, severity, and time filters your application can use. `sinceMs` is a lower bound on `lastObservedAt`, expressed in Unix milliseconds. Incident timestamps answer different questions: | Field | Meaning | | --- | --- | | `occurredAt` | When the incident happened or is forecast to happen | | `firstObservedAt` | When Y2 first observed the incident | | `lastObservedAt` | When Y2 most recently observed or refreshed the incident | A forecast incident can have an `occurredAt` value years in the future while both observation timestamps reflect when Y2 received the supporting information. Use `lastObservedAt`, not `occurredAt`, as an incremental polling watermark. ```bash curl --get "https://api.y2.dev/api/v2/incidents" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "category=cyber" \ --data-urlencode "severity=high" \ --data-urlencode "status=active" \ --data-urlencode "limit=50" \ --data-urlencode "fields[incidents]=title,severity,status,lastObservedAt" ``` List collections return `data`, `meta`, and `links`. Follow `links.next` until it is `null`; the cursor is opaque and bound to the request's filters. Limits are clamped to `1–500`. For incremental polling, convert the largest returned `lastObservedAt` value to Unix milliseconds and pass it on the next request: ```bash curl --get "https://api.y2.dev/api/v2/incidents" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "sinceMs=1786374126243" \ --data-urlencode "fields[incidents]=title,status,occurredAt,firstObservedAt,lastObservedAt" ``` ## Drill into related context An incident or entity detail request returns its primary resource by default. Use `include` to request only the bounded related collections you need. ```bash curl --get \ "https://api.y2.dev/api/v2/incidents/inc_0123456789abcdef01234567" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "include=observations,entities,primaryPlace" \ --data-urlencode "fields[incidents]=title,severity,status,lastObservedAt" \ --data-urlencode "fields[observations]=title,severity,observedAt,geometry" ``` Supported includes are `observations`, `markets`, `entities`, `primaryPlace`, and `relatedIncidents`. ```bash curl --get \ "https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "include=relations,relatedEntities,incidents" \ --data-urlencode "fields[entities]=kind,name,aliases,updatedAt" ``` Supported includes are `relations`, `relatedEntities`, `incidents`, `markets`, `observations`, `companyIdentity`, and `financialObservations`. The last two apply to organization and vendor entities; financial observations are scoped to the key's workspace. Sparse fieldsets use `fields[incidents]`, `fields[entities]`, `fields[markets]`, `fields[observations]`, `fields[finint]`, or `fields[signals]` as appropriate. The server always retains resource identity fields and rejects unsupported fields or includes. A request can contain at most 24 values in each projection parameter. ## Traverse a graph Use the general entity graph after resolving an `ent_...` ID from `/entities`. Its breadth-first traversal accepts `depth=0–3`, supports a comma-separated `relationKinds` filter, and is capped at 200 nodes. ```bash curl --get \ "https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/graph" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "depth=2" \ --data-urlencode "relationKinds=affects,targets,uses" ``` For a CVE, threat actor, or malware family, `/cyber/graph` offers a cyber-specific traversal. Pass at least one of `rootCveId`, `rootActorId`, or `rootMalwareFamilyId`; its depth is limited to `1–2`. ## Follow a company across cyber and market events Company endpoints read workspace research, so they require a workspace-bound API key and never accept x402 payment. `GET /entities/{entityId}/financial-observations` pages through the workspace's recorded financial facts for an organization or vendor, newest recorded first. Filter with `metricKey` (an index) or `factType` (applied per page, so an empty page can still carry a next cursor). Each fact keeps its reporting period, recorded time, source provenance, and, for SEC imports, the retained JSON record pointer. History reflects what Y2 retained, not complete filing coverage. `GET /entities/{entityId}/fusion` places cyber, market, supply-chain, and geopolitical events about the company and the parties on its exposure path on one timeline: ```bash curl --get \ "https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/fusion" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "windowDays=90" \ --data-urlencode "adjacencyDays=7" ``` | Field | Meaning | | --- | --- | | `counterparties` | Suppliers, customers, parents, subsidiaries, and operated systems, from catalog ties or evidenced ledger claims (`provenance`) | | `timeline` | Dated events, newest first, each with `domain`, `path`, `provenance`, and a public `source` ID | | `undated` | Events without a source time, kept visible instead of placed at a guessed date | | `adjacencies` | A market event that followed a cyber event within `adjacencyDays`; timing, not causation | | `topics` | Workspace topics whose subject sits on the exposure path | | `coverage` | Read budgets that were reached and what each source does not cover | See [Cyber-market fusion](/docs/ontology/explanation/cyber-market-fusion) for what the view claims and what it does not. ## Select the correct scope | Scope | Accepted operations | | --- | --- | | `intel:explorer` | Entities, entity details and graphs, cyber-market fusion, unfiltered incidents, and general signals | | `intel:finint` | Markets, FININT indicators, company financial history, non-cyber categorized incidents, and markets or supply-chain signals | | `intel:cyber` | Cyber graph, CVE and actor lists, cyber incidents, and cyber or technology signals | When `/signals` has no `domain` filter, any Intel scope is accepted. Other signal domains require `intel:explorer`. Authenticated requests can receive authorized private and workspace rows; x402 callers receive global and community rows only. New API keys with Intel scopes require Pro or Elite. Existing Lite API keys remain valid under the current grace policy, while Lite retains its in-app Intel surfaces. See [Plans and Limits](/docs/plans) for the complete product-entitlement matrix. ## Authenticate or pay per request Intel endpoints accept a scoped bearer API key or their documented [x402 payment flow](/docs/api/x402), except company financial history and fusion, which read workspace research and require a workspace-bound key. A request without `Authorization` begins with `402 Payment Required`; retry it with the signed payment header described by that endpoint. Do not send both a bearer key and x402 payment for the same request. ## Intel v2 or OSINT v1? | Use Intel v2 when you need | Use [OSINT v1](/docs/api/osint) when you need | | --- | --- | | Stable entity and incident identity | Broader normalized event feeds | | Relationships and graph traversal | Source-health and source-specific surfaces | | Bounded related-resource expansion | Country, regional, and geospatial feeds | | Semantic FININT and decision signals | NDJSON or GeoJSON representations where documented | ## Endpoint reference --- # Connect Y2 through MCP (/docs/api/mcp) Y2 Intel offers a hosted, read-only connection for assistants and a local MCP package for developers. Hosted connections let you choose one workspace and approve permissions in Y2 without copying an API key into your assistant. ## Connect in Claude or ChatGPT Use the following URL when your assistant supports adding a custom remote MCP connection: ```text https://y2-intel-mcp.managed-services.workers.dev/mcp ``` 1. Open your assistant's app or connector settings and add **Y2 Intel** with the server URL above. Availability of custom connections depends on the assistant and workspace settings; your administrator may need to enable them. 2. Follow the sign-in prompt to Y2. Confirm the client identifier and return destination match the assistant where you started the connection. 3. Choose the Y2 workspace you want to connect. Keep only the requested read permissions you need, then select **Allow selected access**. 4. Return to your assistant and ask it to check the Y2 connection or list your intelligence profiles. An empty profile list means the selected workspace has no accessible profiles. You need an existing Y2 account, workspace membership, and connected intelligence access in that workspace. The consent page shows the permissions currently available. Native cards and maps require an assistant that supports MCP Apps; other clients receive structured results and text. A custom connection does not establish a public directory listing or marketplace approval. ### What a hosted connection can do | Permission | Available information | | --- | --- | | Intelligence profiles | Profiles, monitoring topics, and profile details | | Reports and sources | Existing reports and their citations | | Maps and regional events | Regional intelligence and event map layers | | Entities and intelligence signals | Entity details, relationships, and signals | | Financial intelligence signals | Financial and market signals | | Cyber intelligence signals | Cyber threat and vulnerability signals | The assistant can display profile cards, signal lists, and maps within the capabilities of its host. Hosted tools do not create or edit profiles, start report generation, send messages, or perform purchases. Each tool checks the approved permissions and your current workspace access. A connection to one workspace cannot read a different workspace. Try prompts such as: - “List the Y2 Intel profiles in my connected workspace.” - “Show the latest report for this profile, with source citations.” - “Show regional events for this country on a map.” - “Show recent signals about this entity, with dates and sources.” ### Disconnect, reconnect, or change permissions Open [Settings → Connections](/app/settings/connections) in Y2 to see your connections, their workspace, permissions, and expiration. Choose **Disconnect** to stop new access immediately. Connections expire after 30 days. To change a workspace or permissions, disconnect and start a new connection from your assistant. Reconnect if the assistant reports that authorization has expired or been revoked. Information returned by a tool is shared with your chosen assistant provider under its policies. Disconnecting stops future access; it does not erase copies already received by that provider. Y2 does not send your Y2 password or internal API credential to the assistant. Read the [Privacy Policy](/privacy) for processing, retention, and deletion information. If sign-in finishes but the assistant does not connect, start a fresh connection in the same browser and complete it before the consent request expires. An unavailable permission means your current workspace access does not include that capability. Contact the workspace administrator if expected access is missing. For other connection issues, contact [support@y2.dev](mailto:support@y2.dev) with the assistant name and error text; never include tokens or credentials. ## Local MCP package `@y2-intel/mcp` 0.2.0 gives local stdio agents access to Y2 docs, the live OpenAPI contract, 52 default tools, and optional action tools. The MCP server is distributed separately from the Y2 platform. Its `tools/list`, `resources/list`, and `prompts/list` responses are the live source of truth. ## Quickstart ### Before you begin You need Node.js 20 or later and an MCP client with stdio support. Public discovery works without a key. For authenticated tools, create a separate least-privilege key per client and provide it as `Y2_API_KEY`; never put a key in a prompt or tool argument. ### Verify the package without a key Ask the installed server to load the live OpenAPI document and list its operations: ```bash npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \ -- \ --method tools/call \ --tool-name y2_list_api_operations ``` This proves that the server starts and can read Y2's live API contract. ### Configure your client Add the stdio server from a shell where `Y2_API_KEY` is set: ```bash codex mcp add y2 --env Y2_API_KEY="$Y2_API_KEY" -- npx -y @y2-intel/mcp ``` Confirm that the configuration exists: ```bash codex mcp list ``` The `--env` value is saved with the MCP server configuration. Protect the client configuration file as a secret. Add the stdio server from a shell where `Y2_API_KEY` is set: ```bash claude mcp add --transport stdio --env Y2_API_KEY="$Y2_API_KEY" y2 \ -- npx -y @y2-intel/mcp ``` Inspect the saved server: ```bash claude mcp get y2 ``` Add the server to the client's MCP configuration and restart the client: ```json { "mcpServers": { "y2": { "command": "npx", "args": ["-y", "@y2-intel/mcp"], "env": { "Y2_API_KEY": "y2_..." } } } } ``` This configuration contains a plaintext credential. Restrict access to the configuration file or use the client's supported secret-management facility. Configure your harness or agent to launch the Y2 server as a local stdio process: ```bash Y2_API_KEY="$Y2_API_KEY" npx -y @y2-intel/mcp ``` Use the client's environment or secret-management facility to provide `Y2_API_KEY`. The client must keep stdin and stdout connected to the process for MCP transport. For public documentation and OpenAPI discovery only, omit `Y2_API_KEY`. ### Verify the installed capabilities Inspect the tool schemas before allowing authenticated calls: ```bash npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \ -- \ --method tools/list ``` Version 0.2.0 exposes 52 tools by default. Test `y2_list_reports` before enabling action tools. ## Call any Y2 API operation For any endpoint without a typed MCP tool: 1. Call `y2_list_api_operations` to discover the endpoint's current `operationId`. 2. Call `y2_call_api` with that `operationId`, its `parameters`, and an optional JSON `body`. For example: ```json { "operationId": "listReports", "parameters": { "limit": 10 } } ``` `y2_call_api` builds path, query, header, and JSON body values from the live spec. Authorization follows the selected operation. Use `y2_get_openapi_operation` to inspect its full schema first. `Y2_MCP_ENABLE_WRITE_TOOLS=false` hides dedicated mutation tools, but it does not remove scopes from `Y2_API_KEY`. Give general-purpose agents read-only keys unless they are intended to call write operations. ## Tool reference ### OpenAPI-driven tools | Tool | Purpose | Y2 authorization | | --- | --- | --- | | `y2_list_api_operations` | List current operation IDs, methods, and paths | None | | `y2_call_api` | Call an operation using its live OpenAPI definition | Per-operation scopes | | `y2_get_openapi_operation` | Inspect one operation's parameters and schemas | None | ### Typed read tools Use `tools/list` for exact names and schemas, and `y2_list_api_operations` for the current API. | Group | Included tools | Scope | | --- | --- | --- | | Reports (7) | List and get reports; signals, graph, text, audio, and audio transcript | `reports:read`, `reports:audio` | | News (3) | News, recaps, and feeds | `news:read` | | OSINT (18) | Events, map, CII, country intelligence, military posture, aircraft, vessels, GPS jamming, source status, cyber threats, prediction markets, regional search, and FinInt indicators | `osint:read` | | Intel v2 (13) | Incidents, entities and graphs, markets, FinInt, signals, cyber graph, CVEs, threat actors, changes, and knowledge retrieval | `intel:explorer`, `intel:finint`, `intel:cyber` | | Account and workflow reads (7) | Profiles, webhooks, projects, automations, and automation runs | `profiles:read`, `webhooks:manage`, `projects:read`, `automations:read` | | Public (1) | `y2_get_x402_receipt` | None | The four resources remain `y2://docs/index`, `y2://docs/full`, `y2://openapi`, and `y2://quickstart`. The prompts remain `integrate-y2-api`, `ask-y2-brief`, and `debug-y2-api-call`. ### Optional action tools `Y2_MCP_ENABLE_WRITE_TOOLS=true` adds 14 typed mutations: profile create/update/patch/delete, project create/patch, automation create/patch/run, webhook create/update/delete/test, and subscription delivery update. Each still requires its matching scope. `Y2_MCP_ENABLE_AGENT=true` adds `y2_ask_agent` and requires `agent:y2`. `agent:y2` allows the fixed Y2 agent to use tools available under the key's scopes and plan. Omit this scope and leave `Y2_MCP_ENABLE_AGENT` disabled unless those actions are intended. ## Configuration reference The package accepts these environment variables: Both feature flags accept `true` or `1`. Override base URLs only for a Y2-controlled development or test environment. Do not point a credentialed MCP process at an untrusted proxy. ## Troubleshooting | Symptom | Check | | --- | --- | | Server exits immediately | Run `npx -y @y2-intel/mcp` in a terminal and inspect stderr | | No Y2 server in the client | Confirm the client saved the configuration and restart it | | Tool returns `401` | Replace a missing, malformed, revoked, or expired `Y2_API_KEY` | | Tool returns `403` | Create a new key with the required scope; key scopes are fixed at creation | | Tool returns `429`, `502`, `503`, or `504` | The server already retries twice and honors `Retry-After`; a surfaced response means the retry budget was exhausted | | Agent tool is missing | Set `Y2_MCP_ENABLE_AGENT=true`, restart the client, and confirm `tools/list` includes `y2_ask_agent` | | Typed mutation tool is missing | Set `Y2_MCP_ENABLE_WRITE_TOOLS=true`, restart the client, and inspect `tools/list` | | Agent tool returns a credit error | Check the workspace Agent Y2 credit balance and plan access; it does not use x402 | --- # Migrate to Canonical API Responses (/docs/api/migration-v1-1) Use this guide to migrate a client that depends on legacy persistence-shaped fields or pagination behavior. The v1 routes remain at `/api/v1`; their response contract was widened additively. Intel v2 routes under `/api/v2` emit canonical public DTOs. Responses identify the route and schema contracts with `X-Y2-API-Version` and `X-Y2-Schema-Version`. Current shared v1 response helpers emit API version `1.0` and schema version `1.1`; v2 operations emit API version `2.0`. ## Migration checklist ### Capture current client fixtures Save representative success, empty, error, and paginated responses before changing parsers. Include response headers in the fixtures. ### Store public resource IDs Replace saved Convex document IDs with the typed IDs emitted in current responses. Treat every ID as opaque. ### Adopt canonical fields Read RFC 3339 semantic timestamps, typed outcome rows, Problem Details, and the documented missing-value states. Stop creating new dependencies on deprecated aliases. ### Replace collection traversal Follow `links.next` or return `meta.page.nextCursor` unchanged. Do not infer another page from the number of rows. ### Harden writes and delivery consumers Add idempotency keys, ETag preconditions, CloudEvent deduplication, and a durable change-feed checkpoint where those features apply. ## Replace persistence IDs with public IDs | Resource | Prefix | | --- | --- | | Report | `rpt_` | | Profile | `prf_` | | Subscription | `sub_` | | Webhook | `whk_` | | Observation | `obs_` | | Incident | `inc_` | | Entity | `ent_` | | Market | `mkt_` | | Financial indicator row | `fin_` | | Place | `plc_` | | Relation | `rel_` | | Signal | `sig_` | | Source | `src_` | Legacy Convex IDs remain accepted on migrated resource paths during the compatibility window, but responses and webhooks emit public IDs. Store the exact ID Y2 returns. Do not derive IDs, parse the hash, or depend on its current length. ## Traverse collections by link or cursor Canonical collections return `data`, `meta`, and `links`. `meta.pageCount` is the number of rows in the current page, not the collection total. A total exists only where a response explicitly defines `totalCount`. ```ts let url: string | null = "https://api.y2.dev/api/v2/incidents?limit=100"; while (url) { const response = await fetch(url, { headers: { Authorization: `Bearer ${process.env.Y2_API_KEY}` }, }); if (!response.ok) throw await response.json(); const page = await response.json(); await persist(page.data); url = page.links.next ? new URL(page.links.next, "https://api.y2.dev").toString() : null; } ``` Cursors are opaque and filter-bound. Never edit them or reuse one with a different filter shape. Changing `limit` does not change the filter shape, but following `links.next` is the safest default because it preserves the server's continuation parameters. ## Distinguish omitted, null, and empty | State | Meaning | | --- | --- | | Field omitted | Optional enrichment or bounded relation was not requested or not present | | Field is `null` | The schema permits the field, but its value is unknown or unavailable | | Field is `[]` | The relationship or outcome set is known and empty | Numeric measurements include their documented unit, currency, or provider basis. Prediction outcomes are objects with `label` and a `0–1` `probability`; do not parse JSON-encoded outcome strings in a new client. ## Replace deprecated compatibility fields | Surface | Deprecated | Canonical replacement | | --- | --- | --- | | Regional OSINT query | `since` | `datetime={RFC3339 start}/..` | | Regional OSINT query | `until` | `datetime=../{RFC3339 end}` | | Profile report metadata | `reportGeneratedAt` | `reportGeneratedAtISO` | | Profile delivery metadata | `generatedAt` | `generatedAtISO` | | Report audio metadata | `duration` | `durationSeconds` | | Collection metadata | `meta.count` | `meta.pageCount` for this page | The regional `since` and `until` parameters are inclusive epoch-millisecond bounds. Do not combine either one with `datetime`. The compatibility fields remain additive on v1 for the published migration window; new integrations should use only the canonical fields. ## Parse Problem Details JSON failures use `application/problem+json` with RFC 9457-style top-level fields: `type`, `title`, `status`, `detail`, `instance`, `code`, `requestId`, and `resolution`. Read `resolution` for a safe next step; no nested compatibility error object is returned. ```ts if (!response.ok) { const problem = await response.json(); logger.error({ code: problem.code, status: problem.status, requestId: problem.requestId, resolution: problem.resolution, }); throw new Error(problem.detail); } ``` Retry only when the status and response headers indicate a retryable rate-limit or availability failure. A validation, authentication, scope, idempotency, or precondition failure requires a request change. ## Request specialized representations explicitly - Send `Accept: text/markdown` to a report endpoint that advertises canonical Markdown. - Send `format=ndjson` or `Accept: application/x-ndjson` only to collections whose OpenAPI operation advertises NDJSON. - Send `format=geojson` or `Accept: application/geo+json` only to spatial operations that advertise a GeoJSON FeatureCollection. Do not infer support from another collection. For example, `/api/v1/osint/finint` offers NDJSON, while `/api/v2/finint` currently returns JSON only. GeoJSON follows WGS 84 and uses `[longitude, latitude]`. For `/api/v1/osint/regional`, `bbox` is `west,south,east,north`; `datetime` accepts an RFC 3339 instant or `start/end` interval, with `..` as an open boundary. ## Make profile and webhook writes safe `POST /profiles` and `POST /webhooks` return `201`, the created resource, `Location`, and `ETag`. Both accept an `Idempotency-Key` containing 8–200 allowed characters. The key is scoped to the tenant and operation and retained for 24 hours. - Replaying the same key with the same canonical JSON body returns the original resource. - Reusing the key with a different body returns `409 IDEMPOTENCY_CONFLICT`. - `PUT` replaces mutable state; `PATCH` changes only supplied fields. - Updates and deletes accept `If-Match`; a stale value returns `412 PRECONDITION_FAILED` with the current `ETag`. - A successful delete returns `204` with no JSON body. ## Migrate webhook consumers to CloudEvents Report notifications use `Content-Type: application/cloudevents+json` and event type `dev.y2.report.generated.v1`. The body contains compact report and subscription IDs, a bounded summary, intelligence counts, audio availability, and API links. It excludes report HTML, storage IDs, model names, prompts, and generation costs. Deduplicate on the CloudEvent `id`, also sent as `Idempotency-Key` and `X-Y2-Event-Id`. When the same logical event is attempted again, its event ID and body remain stable; `X-Y2-Attempt-Id`, `X-Y2-Attempt`, and `X-Y2-Timestamp` identify the network attempt. Verify `X-Y2-Signature` over the exact raw bytes before parsing JSON. Y2 does not run an independent webhook retry loop. A failed attempt increments the consecutive failure count, and five consecutive failures disable the webhook. Use the change feed to repair missed state. ## Recover with the change feed `GET https://api.y2.dev/api/v2/changes` returns append-only resource changes from an exclusive watermark. Process each page, commit every resource update or tombstone, and persist `meta.watermark` as the final write. Deleted resources have `changeType: "deleted"` and `tombstone: true`. Authenticated keys receive global intelligence changes plus their user or workspace changes. Anonymous x402 requests receive global changes only. The current feed has no retention expiry, but consumers should still repair continuously instead of treating it as a permanent archive guarantee. --- # Build with the News API (/docs/api/news) Use the News API to add current narrative context to dashboards, alerts, reports, or intelligence pipelines. The API reads Y2's cached direct-source terminal data; it does not run a new source fetch for each request. ## Choose an output | Need | Operation | | --- | --- | | Discover the current topic catalog | `GET /api/v1/news/feeds` | | Page individual source items | `GET /api/v1/news` | | Read a synthesized topic recap | `GET /api/v1/news/recaps` | The News base URL is: ```text https://api.y2.dev/api/v1/news ``` ## Authenticate Send a bearer key with `news:read`: ```bash curl "https://api.y2.dev/api/v1/news/feeds" \ --header "Authorization: Bearer $Y2_API_KEY" ``` All three News operations also support [x402 pay-per-request](/docs/api/x402). Without a bearer key, the first request returns `402 Payment Required`; retry with the payment header described in that response. Do not send a bearer key and x402 payment together. ## Discover feed IDs Call `GET /feeds` instead of hard-coding the catalog. The current registry contains 40 topics grouped across crypto, AI and technology, macro and finance, politics and geopolitics, industry, and regional desks. ```bash curl "https://api.y2.dev/api/v1/news/feeds" \ --header "Authorization: Bearer $Y2_API_KEY" ``` Each `data` row contains: | Field | Use | | --- | --- | | `id` | Machine-readable value for the `topics` query parameter | | `name`, `shortLabel` | Full and compact display labels | | `description` | Human-readable feed purpose | | `group`, `groupLabel` | Picker grouping metadata | | `color` | Current UI gradient classes; do not use as identity | | `ingestOntology` | Whether eligible items can enter Y2's ontology pipeline | `meta.defaultTopics` currently contains `crypto`, `geopolitics`, `macro`, `equities`, `ai`, and `energy`. Omitting `topics` from item or recap requests uses that set. ## Page news items Pass one or more comma-separated feed IDs. The server merges matching caches, removes duplicate source items by their upstream ID, sorts newest first, and returns up to 200 rows. ```bash curl --get "https://api.y2.dev/api/v1/news" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "topics=cyber,semiconductors,ai" \ --data-urlencode "limit=100" ``` The JSON response has `data`, `meta`, and `links`. Follow `links.next` until it is `null`; do not edit the opaque cursor or reuse it with different topic filters. A canonical news item includes: Treat source URL, publisher, language, and retrieval metadata as nullable. A source record can be identified even when the provider did not supply every attribution field. ## Stream rows as NDJSON For a warehouse or line-oriented processor, request NDJSON. Each non-empty line is one canonical news item; continuation moves to `X-Y2-Next-Cursor` because the body contains rows only. ```bash curl --get "https://api.y2.dev/api/v1/news" \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Accept: application/x-ndjson" \ --data-urlencode "topics=geopolitics,energy" \ --data-urlencode "limit=200" ``` You can also send `format=ndjson`. Preserve the original topics and limit when sending the next cursor. ## Retrieve topic recaps Use recaps when an application needs synthesis rather than individual items. Supported timeframes are `12h`, `24h`, `3d`, and `7d`; the default is `12h`. ```bash curl --get "https://api.y2.dev/api/v1/news/recaps" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "topics=macro,equities,rates_fx" \ --data-urlencode "timeframe=24h" ``` `data` is an object keyed by topic ID. A requested topic can be absent when no valid cached recap exists for that topic and timeframe. Use `meta.topics` and `meta.timeframe` to retain request context; do not treat a missing topic as an empty recap. `/news` and `/news/recaps` can return `503 CACHE_NOT_READY` before their respective caches have any data. Retry with backoff. A successful empty or partially populated recap object means the cache exists but not every requested topic has a current recap. ## Combine News with structured intelligence News items are narrative observations. Use [OSINT v1](/docs/api/osint) for broader normalized event, country, geospatial, and source-health surfaces. Use [Intel v2](/docs/api/intel) when you need stable incidents, entities, relationships, FININT, or extracted decision signals. --- # Ontology Ledger API (/docs/api/ontology-ledger) The ledger API reads and writes your workspace's passive entity ledger. It requires a **workspace-bound** API key and does not accept x402 payment. For the concepts, start with [Ontology and Cyber-Market Fusion](/docs/ontology). The base URL is: ```text https://api.y2.dev/api/v2/ledger ``` ## Operations | Goal | Operation | Scope | | --- | --- | --- | | List subjects, optionally anchored to one entity | `GET /subjects?entityId=ent_…` | `ledger:read` | | Open a subject, optionally anchored | `POST /subjects` | `ledger:write` | | Fold one subject at the requested clocks | `GET /subjects/{subjectId}?recordedAt=&validAt=` | `ledger:read` | | Export one subject as STIX 2.1 | `GET /subjects/{subjectId}/stix` | `ledger:read` | | Export sealed lines | `GET /records?afterSeq=&limit=` | `ledger:read` | | Append a designator, observation, or claim | `POST /records` | `ledger:write` | | Verify the seal over a window | `GET /verify?afterSeq=&limit=` | `ledger:read` | Writes also require an owner, admin, or member role for the key's user. ## Append a record `POST /records` takes one record. The `kind` must match the payload key: ```json { "kind": "observation", "observation": { "method": "public-registry", "sourceGrade": "primary", "sourceUrl": "https://filings.example/northwind/annual", "retrievedAt": "2026-06-01T11:40:00Z", "excerpt": "Northwind relies on Contoso for fulfillment.", "collector": "filings-sync" } } ``` `contentHash` is computed from the excerpt. Send it only when the excerpt is absent and `archiveUrl` holds the hashed content. Send `retrievedAt` when you fetched the page earlier; omitted on a retrieval, it defaults to the time the line is recorded. ```json { "kind": "designator", "designator": { "kind": "external-id", "namespace": "sec-cik", "value": "320193" } } ``` Values are canonicalized: this CIK is stored as `0000320193`. An existing designator with the same key is returned with `200` and `created: false`. ```json { "kind": "claim", "claim": { "predicate": "supplies", "from": "sbj_…supplier", "to": "sbj_…issuer", "confidence": 70, "verification": "located", "validFrom": "2025-12-31T00:00:00Z", "evidenceRefs": ["obv_…"], "note": "The issuer's own filing names the supplier." } } ``` Send `supersedes` with the current head's `claimId` for every line after a claim key's first. Send an `Idempotency-Key` header to make a retry safe: a replay returns the original record with `200`. Reusing a key with a different body returns `409 IDEMPOTENCY_CONFLICT`. ## Handle rule failures A record that breaks a ledger rule is rejected before anything is written. `detail` lists each rule code and message. | Status | When | | --- | --- | | `409 CONFLICT` | `assertion.chain`: the line does not supersede the current head of its claim key | | `400 VALIDATION_ERROR` | Any other rule, such as `assertion.verification` or `designator.value` | | `404 NOT_FOUND` | The anchored entity or subject does not exist | | `403 FORBIDDEN` | Personal key, missing role, or suspended seat | On `409`, fold the subject again to read the current head, then retry with `supersedes` set to it. ## Keep a copy in sync Page `GET /records` from `afterSeq=0` and store each `line` verbatim. Resume from `meta.nextAfterSeq`. Because the log is append-only and sealed, a synced copy stays valid, and the offline checker can prove it matches what Y2 recorded. See [Export STIX and verify the seal](/docs/ontology/how-to/export-and-verify). ## Read the fused view Cyber-market fusion is an entity operation in the [Intel API](/docs/api/intel): `GET /api/v2/entities/{entityId}/fusion` with `intel:explorer`. It uses the ledger's anchored subjects and supply ties to build the exposure path. ## Endpoint reference --- # Use the OpenAPI Contract (/docs/api/openapi) Y2 publishes one OpenAPI 3.1 document as the machine-readable contract for its REST API. The generated endpoint reference, API Workbench, repository TypeScript declarations, and contract tests all consume that source. ```mermaid flowchart LR A[public/api/openapi.yaml] --> B[Fumadocs endpoint pages] A --> C[API Workbench] A --> D[TypeScript declarations] A --> E[Response fixtures and contract tests] B --> F[Generated reference navigation] ``` Use the generated [Endpoint Reference](/docs/api/reference) for operation details. Use this page to locate, consume, or update the raw specification. Do not manually edit files under `content/docs/api/reference` or `src/lib/openapi-reference-nav.generated.ts`. ## Get the specification The production URL is: ```text https://y2.dev/api/openapi.yaml ``` The app serves the same repository file at `/api/openapi.yaml` in development and production. Agents and function-calling clients can use the equivalent JSON representation at: ```text https://y2.dev/openapi.json ``` [Download the OpenAPI specification](/api/openapi.yaml) ## Compose the correct request URL The document contains more than one server because v1 and v2 use different path shapes. | Operation shape | Server | Example result | | --- | --- | --- | | v1 path such as `/reports` | `https://api.y2.dev/api/v1` | `https://api.y2.dev/api/v1/reports` | | v2 literal path such as `/api/v2/incidents` | `https://api.y2.dev` operation override | `https://api.y2.dev/api/v2/incidents` | OpenAPI tooling should honor an operation- or path-level `servers` value before the document-level servers. Avoid blindly prepending `/api/v1` to an Intel v2 path. ## Read Y2 extensions In addition to standard OpenAPI fields, operations can declare: The standard `security` array remains authoritative for allowed authentication alternatives. Agent Y2 operations are bearer-key only. Operations with an empty security alternative and `x-x402` begin the payment flow without `Authorization`. The sanitized x402 receipt lookup is public. ## Import into an API client ### Import the production URL Import `https://y2.dev/api/openapi.yaml` into a client that supports OpenAPI 3.1. ### Configure the server Confirm that the selected operation uses the intended v1 or v2 server. Some clients expose the document's server list as an environment selector. ### Add a scoped credential Store `Y2_API_KEY` in the client's secret or environment facility and configure bearer auth as `Bearer {{Y2_API_KEY}}`. Never commit an exported collection containing the key. ### Validate a read Start with a bounded GET whose required scope is on the key. Check `X-Request-Id`, API version, schema version, and rate-limit headers as well as the body. ## Generate types or a client For a TypeScript project, generate path and component types directly: ```bash npx openapi-typescript https://y2.dev/api/openapi.yaml \ --immutable \ --alphabetize \ --output src/y2-api.d.ts ``` For a full client, use an OpenAPI 3.1-compatible generator and review its handling of operation-level servers, streaming responses, `application/problem+json`, NDJSON, GeoJSON, and x402 extensions. Generated code is a starting point; SSE and payment flows commonly need explicit transport code. ## Update the contract in this repository The editable source is `public/api/openapi.yaml`. ### Change the source specification Update the operation, schema, response, examples, scopes, and x402 metadata together. Keep operation IDs stable unless the operation itself is intentionally replaced. ### Regenerate derived artifacts ```bash bun run docs:openapi bun run generate:api-types bun run generate:api-contract-fixtures ``` These commands update the Fumadocs operation pages and nav, immutable TypeScript declarations, and schema-derived response fixtures. ### Run the contract gate ```bash bun run check:api-contract bun run docs:source ``` The contract gate lints the OpenAPI file, checks generated types, verifies the 54-operation manifest against Convex HTTP registrations, validates response fixtures against schemas, and runs focused tests for IDs, pagination, representations, projections, middleware, and webhook identity. ### Review generated scope Confirm that generated changes correspond to the intended source edit. Never patch a generated endpoint page to conceal a mismatch in `public/api/openapi.yaml`. `generate:api-contract-fixtures` derives representative bodies from the schemas. The contract tests prove schema consistency and prevent persistence-field leakage; they do not prove that a deployed environment currently returns those exact values. ## Repository artifacts --- # Query the OSINT API (/docs/api/osint) The OSINT API exposes source-oriented Situation Room data as canonical public resources. Use it for normalized event feeds, geospatial observations, country surfaces, provider health, prediction markets, transport and security feeds, or financial indicators. The base URL is: ```text https://api.y2.dev/api/v1/osint ``` ## Choose an endpoint ### Events and geography | Goal | Endpoint | Useful filters | | --- | --- | --- | | List normalized threat events | `GET /events` | `category`, `severity` | | Load only geolocated map events | `GET /map` | `region` | | Compose regional and spatial filters | `GET /regional` | `q`, `region`, `countryCode`, `sourceType`, `bbox`, radius, time | | Read report-extracted events | `GET /y2-events` | `category`, `severity`, `countryCode` | ### Country intelligence | Goal | Endpoint | | --- | --- | | Periodically generated intelligence brief | `GET /countries/{countryCode}/brief` | | Primary stock index and weekly change | `GET /countries/{countryCode}/markets` | | Country-linked prediction markets | `GET /countries/{countryCode}/predictions` | | Recent news and report observations attributed to a country | `GET /countries/{countryCode}/news` | | Country Conflict Indicators Index | `GET /countries/{countryCode}/cii` | Country codes are ISO 3166-1 alpha-2 values such as `US` or `UA`. ### Specialized feeds | Family | Endpoints | | --- | --- | | Conflict and security | `/cii`, `/cyber-threats`, `/military-posture`, `/gps-jamming` | | Transport | `/aircraft`, `/vessels` | | Markets | `/prediction-markets`, `/finint` | | Operations | `/sources/status` | Aircraft ingestion has been disabled since May 8, 2026, to reserve the shared Wingbits quota for GPS interference detection. `/aircraft` and `/military-posture` can return empty or stale data. `/gps-jamming` is unaffected. ## Authenticate or use x402 Every published OSINT operation accepts a bearer key with `osint:read` or its documented [x402 pay-per-request flow](/docs/api/x402). ```bash curl "https://api.y2.dev/api/v1/osint/events?limit=50" \ --header "Authorization: Bearer $Y2_API_KEY" ``` A request without `Authorization` begins with `402 Payment Required`. Retry it with the signed payment header described in that response; do not send bearer and x402 credentials together. ## Search regional events `/regional` combines text, source, category, severity, region, country, time, and coordinate filters with AND semantics. Events without coordinates remain unless you request a bounding box, radius, or `requireCoordinates=true`. ```bash curl --get "https://api.y2.dev/api/v1/osint/regional" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "q=port disruption" \ --data-urlencode "sourceType=rss" \ --data-urlencode "category=economic" \ --data-urlencode "bbox=-10.0,35.0,40.0,65.0" \ --data-urlencode "datetime=2026-07-01T00:00:00Z/2026-07-21T23:59:59Z" \ --data-urlencode "limit=100" ``` `q` searches title and falls back to description substrings. `bbox` is exactly `west,south,east,north` in WGS 84 and cannot cross the antimeridian. Radius search requires all three of `nearLat`, `nearLon`, and `radiusKm`; the maximum radius is 20,000 km. Use `datetime` for new integrations. It accepts an RFC 3339 instant or inclusive `start/end` interval; `..` creates an open boundary. The inclusive epoch-millisecond `since` and `until` parameters are deprecated and cannot be combined with `datetime`. ## Read country news Use a two-letter country code to retrieve recent news and report observations: ```bash curl "https://api.y2.dev/api/v1/osint/countries/SA/news?limit=8" \ --header "Authorization: Bearer $Y2_API_KEY" ``` The country filter uses the observation's geographic attribution. A Saudi pipeline event can qualify for `SA`; a regional Red Sea event does not qualify merely because Saudi Arabia is mentioned or exposed to its effects. Available geographic evidence appears in `provenance.geoResolution`, including the resolution method, matched text, and related or mentioned country codes when available. These associations do not expand the country filter. Country news includes unexpired `rss`, `gdelt`, `acled`, `news_terminal`, and `y2_report` observations, excluding seismic and weather categories. Source and expiry filters apply before the page limit, so satellite detections do not consume the news window. The default `limit` is 8 and the maximum is 20; follow `links.next` to continue. The API returns canonical observations with stable `obs_` IDs. The country card can deduplicate articles for display, so its headline count can differ from the API's observation count. The [News API](/docs/api/news) serves a separate topic-feed cache and requires `news:read`; this country-news endpoint requires `osint:read`. If a country-news cursor is invalid or obsolete, the API returns HTTP 400 with `VALIDATION_ERROR`. Restart without the cursor and follow the new continuation links. Integrations carrying a cursor from before the [September country-news update](/docs/changelog/2026-09#more-dependable-country-news) need to restart their collection after that rollout. A scan that exceeds its read budget returns `QUERY_TOO_BROAD`; neither error is evidence that the country has no news. See the [endpoint reference](/docs/api/reference/osint/getCountryNews) for the full response contract. ## Choose JSON, NDJSON, or GeoJSON Representation support is operation-specific and listed on each generated reference page. JSON collection responses contain `data`, `meta`, and `links`. Follow `links.next` until it is `null`. ```bash curl "https://api.y2.dev/api/v1/osint/cyber-threats?limit=100" \ --header "Authorization: Bearer $Y2_API_KEY" ``` Supported collection routes return one canonical `data` row per line. Read continuation from `X-Y2-Next-Cursor`. ```bash curl "https://api.y2.dev/api/v1/osint/finint?format=ndjson&limit=100" \ --header "Authorization: Bearer $Y2_API_KEY" ``` Spatial routes return an RFC 7946 FeatureCollection. Coordinates are longitude-first, and the next page is the item in `links` whose `rel` is `next`. ```bash curl "https://api.y2.dev/api/v1/osint/map?format=geojson&limit=200" \ --header "Authorization: Bearer $Y2_API_KEY" ``` You can use `Accept: application/x-ndjson` or `Accept: application/geo+json` instead of `format`. Do not assume that every OSINT operation supports every representation: country briefs, country stock indices, country CII, and source status return JSON only. ## Page collections safely JSON collections expose an opaque, filter-bound cursor through `meta.page.nextCursor` and `links.next`. Specialized representations move the cursor to `X-Y2-Next-Cursor` so their body can stay representation-native. Preserve the original filters on the next request. The maximum `limit` depends on the operation: for example, `/map` allows 500, `/regional` allows 200, and country prediction pages allow 10. Use the generated reference instead of assuming one global maximum. ## Check source health before declaring absence If a feed is empty or unexpectedly stale, inspect `/sources/status`: ```bash curl "https://api.y2.dev/api/v1/osint/sources/status" \ --header "Authorization: Bearer $Y2_API_KEY" ``` | Field | Meaning | | --- | --- | | `state` | `closed` normally permits fetching; `open` isolates a failing source; `half_open` is a recovery probe state | | `failureCount` | Consecutive failures; a successful fetch resets it to zero | | `lastSuccessAt`, `lastSuccessAtISO` | Last success in epoch milliseconds and RFC 3339; nullable before any success | | `lastFailureAt`, `lastFailureAtISO` | Most recent failure in both time formats | | `lastError` | Most recent provider error when available | Live-provider circuit breakers open after five consecutive failures and permit one probe after a five-minute cooldown. A `y2_report` row represents report-enrichment health rather than a polled provider: `open` means the newest recorded enrichment error is later than the latest success. A healthy source does not prove completeness, and an empty endpoint does not prove that no real-world event occurred. Preserve source attribution and corroborate high-impact conclusions. ## OSINT v1 or Intel v2? | Use OSINT v1 when you need | Use [Intel v2](/docs/api/intel) when you need | | --- | --- | | Source-oriented normalized observations | Stable entity and incident identity | | Country and provider-specific surfaces | Relationship and graph traversal | | GeoJSON or NDJSON where advertised | Sparse semantic projections and bounded relations | | Provider health and circuit-breaker state | Intel-specific FININT, cyber, or Explorer scopes | --- # Manage Profiles through the API (/docs/api/profiles) Profiles define recurring intelligence research: topic, schedule, status, instructions, and report configuration. Subscriptions connect a profile to a delivery method. The API represents them as separate resources. ```mermaid flowchart LR A[Profile prf_...] -->|schedule generates| B[Reports rpt_...] C[Subscription sub_...] -->|follows| A C -->|delivery preference| D[Email, SMS, or webhook] ``` `GET /api/v1/profiles` lists active subscriptions in the API key's user or workspace context. Every row contains `subscription` and `profile`; it is not a flat catalog of every public profile. ## Required scopes | Operation | Scope | | --- | --- | | List subscribed profiles | `profiles:read` | | Create, replace, patch, or delete an owned profile | `profiles:write` | | Change subscription delivery or manage webhook configurations | `webhooks:manage` | Profile creation and configuration features still follow the active workspace's [plan limits](/docs/plans). API write access does not bypass profile, audio, branding, workspace, or delivery entitlements. ## List subscriptions and profiles ```bash curl "https://api.y2.dev/api/v1/profiles" \ --header "Authorization: Bearer $Y2_API_KEY" ``` Each row has this shape: ```json { "subscription": { "id": "sub_0123456789abcdef01234567", "profileId": "prf_0123456789abcdef01234567", "active": true, "delivery": { "method": "email", "emailAudience": "individual", "webhookId": null } }, "profile": { "id": "prf_0123456789abcdef01234567", "name": "Critical Supplier Risk", "status": "active", "frequency": "daily" } } ``` `profile` can be `null` if the subscribed resource no longer resolves. Save both IDs: `prf_...` identifies research configuration, while `sub_...` identifies delivery preferences. The list is not cursor-paginated today: `links.next` and `meta.page.nextCursor` are always `null`. ## Create an owned profile Creation requires `name`, `topic`, `frequency`, and `scheduleTimeOfDay`. Times are UTC. The server creates the profile as `active`, schedules it, and creates an active subscription for the same tenant context. ```bash curl "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 semiconductor suppliers, ports, export controls, severe weather, labor actions, and cyber incidents.", "frequency": "daily", "scheduleTimeOfDay": "08:00", "tags": ["supply-chain", "semiconductors"] }' ``` Supported frequencies are `daily`, `weekly`, `biweekly`, and `monthly`. Weekly and biweekly profiles can supply `scheduleDayOfWeek`; monthly profiles can supply `scheduleDayOfMonth`. See [Scheduling](/docs/profiles/scheduling) for the product semantics, including the twice-monthly biweekly schedule. A successful create returns `201` with: - the canonical `prf_...` resource in `data` - `Location` containing its canonical API path - `ETag` for that representation - the supplied `Idempotency-Key`, when present Idempotency keys must contain 8–200 letters, numbers, `.`, `_`, `:`, or `-`. They are tenant- and operation-scoped for 24 hours. Reusing a key with a different canonical body returns `409 IDEMPOTENCY_CONFLICT`. ## Patch one or more fields Use `PATCH` when omitted fields must remain unchanged. ```bash curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \ --request PATCH \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Content-Type: application/json" \ --header "If-Match: $PROFILE_ETAG" \ --data '{ "status": "paused", "tags": ["supply-chain", "semiconductors", "paused-review"] }' ``` Mutable fields include the profile's name, topic, status, schedule inputs, community visibility, tags, custom instructions, report structure, search/model/budget/recursion/freshness/audio/tool configuration, and branding template. The generated reference defines each nested input. `PUT` and `PATCH /profiles/{profileId}` change profile configuration only. Use the subscription delivery endpoint with the `sub_...` ID to select email, SMS, or a webhook. ## Replace mutable state Use `PUT` only when sending the full intended mutable profile state. It requires the same four fields as creation. Optional mutable fields omitted from a replacement reset to their defaults or are cleared. ```bash curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \ --request PUT \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "name": "Critical Supplier Risk", "topic": "Monitor current disruption across strategic semiconductor supply chains.", "frequency": "weekly", "scheduleTimeOfDay": "08:00", "scheduleDayOfWeek": "monday", "tags": ["supply-chain"] }' ``` Use the `ETag` returned by create or a previous update as `If-Match` when preventing a lost update. `If-Match` is optional; a stale value returns `412 PRECONDITION_FAILED` and the current ETag. There is currently no `GET /profiles/{profileId}` operation that returns an ETag. ## Change delivery on the subscription Read `subscription.id` from `GET /profiles`, then call: ```bash curl "https://api.y2.dev/api/v1/subscriptions/$SUBSCRIPTION_ID/delivery" \ --request PATCH \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "deliveryMethod": "webhook", "webhookConfigId": "whk_0123456789abcdef01234567" }' ``` Delivery methods are `email`, `sms`, `webhook`, and `both_email_sms`. A webhook delivery requires an active, authorized `whk_...` configuration. For email-capable delivery, `emailAudience` can be `individual` or, on a plan with member seats, `workspace`. ## Retrieve reports for a profile The profile's `links.reports` value is the canonical filtered collection. You can also compose it directly: ```bash curl --get "https://api.y2.dev/api/v1/reports" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "profileId=$PROFILE_ID" \ --data-urlencode "limit=1" ``` This requires `reports:read` in addition to whichever profile scopes the client uses. ## Delete an owned profile ```bash curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \ --request DELETE \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "If-Match: $PROFILE_ETAG" ``` Deletion is permanent. It cancels the scheduled job and deletes the profile's subscriptions, signals, reports, and stored audio before returning `204` with no body. The API returns `404` when the key cannot access or mutate the named profile, which avoids revealing another tenant's resource. In a shared workspace, write-role membership is required. Non-admin members can update or delete only profiles they created; workspace administrators can manage tenant-scoped profiles. --- # Projects API (/docs/api/projects) Projects are owner-private intelligence workspaces. API requests derive the user and workspace from the bearer key; callers cannot select another workspace in the request body. ## Scopes | Operation | Scope | | --- | --- | | List or get Projects | `projects:read` | | Create, update, archive, restore, or pin Projects | `projects:write` | Both scopes require a workspace-bound API key. New API keys are available on Pro and Elite, even though Projects are also available in the Lite application. ## Create a Project ```bash curl -sS "https://api.y2.dev/api/v1/projects" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: project-supply-chain-2026-07" \ -d '{ "name": "Supply chain watch", "objective": "Track disruptions affecting critical suppliers", "instructions": "Prefer primary sources and call out conflicting evidence." }' ``` The response returns a stable `prj_...` ID, `Location`, and `ETag`. Reusing an `Idempotency-Key` with the same canonical JSON body returns the original Project. ## Read and update ```bash curl -sS "https://api.y2.dev/api/v1/projects?status=active&limit=20" \ -H "Authorization: Bearer $Y2_API_KEY" ``` Project collections use the same opaque `nextCursor` and `links.next` pagination contract as other Y2 collections. Send the returned cursor unchanged to retrieve older Projects. Use `PATCH /api/v1/projects/{projectId}` for metadata, view defaults, pinning, and reversible lifecycle changes. Send the latest `ETag` in `If-Match` when coordinating multiple writers. ```bash curl -sS -X PATCH "https://api.y2.dev/api/v1/projects/$PROJECT_ID" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Content-Type: application/json" \ -H "If-Match: $PROJECT_ETAG" \ -d '{"status":"archived"}' ``` Archiving keeps Project data and pauses active Automations bound to that Project. Restore it with `{"status":"active"}`. Exact paths, parameters, request bodies, responses, and schemas App views, private knowledge, evidence, resources, and lifecycle behavior --- # OpenAI-compatible Agent Y2 chat stream (/docs/api/reference/agent-y2/createY2OpenAIChatCompletion) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Agent Y2 (/docs/api/reference/agent-y2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} Stream a Y2 Agent response OpenAI-compatible Agent Y2 chat stream --- # Stream a Y2 Agent response (/docs/api/reference/agent-y2/streamAgentY2Chat) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Create an Automation (/docs/api/reference/automations/createAutomation) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get an Automation definition (/docs/api/reference/automations/getAutomation) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Automations (/docs/api/reference/automations) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List Automation definitions Create an Automation Get an Automation definition Update or archive an Automation List Automation run history Request a manual Automation run --- # List Automation run history (/docs/api/reference/automations/listAutomationRuns) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List Automation definitions (/docs/api/reference/automations/listAutomations) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Update or archive an Automation (/docs/api/reference/automations/patchAutomation) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Request a manual Automation run (/docs/api/reference/automations/runAutomation) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Global Knowledge (/docs/api/reference/global-knowledge) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} Retrieve Y2 Global Knowledge --- # Retrieve Y2 Global Knowledge (/docs/api/reference/global-knowledge/retrieveGlobalKnowledgeV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Endpoint Reference (/docs/api/reference) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} API-key streams for the preconfigured Y2 Agent and compatibility endpoint Report retrieval, text, audio, signals, and ontology graphs Profile subscriptions, ownership, and configuration Owner-private intelligence workspaces in the API key workspace Durable Agent Y2 Automation definitions and run history Webhook configuration for paid workspaces Subscription delivery management News Terminal items, feeds, and AI recaps Situation Room events, feeds, country data, and source health Ontology-backed v2 incidents, entities, markets, financial indicators, emergent signals, and cyber graphs. Bounded, provider-neutral semantic and lexical retrieval across authorized Y2 reports and signal assessments. x402 receipt lookup The workspace passive entity ledger: an append-only, SHA-256-sealed log of subjects, designators, public observations, and claims with verification states and valid-time intervals. --- # Cyber entity graph (/docs/api/reference/intel/getCyberGraphV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get cyber-market fusion for a company (/docs/api/reference/intel/getEntityFusionV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Traverse an entity relationship graph (/docs/api/reference/intel/getEntityGraphV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get an intel entity (/docs/api/reference/intel/getEntityV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get an intel incident (/docs/api/reference/intel/getIncidentV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Intel (/docs/api/reference/intel) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} Read the public intelligence change feed List intel incidents Get an intel incident List intel entities Get an intel entity List workspace company financial history Get cyber-market fusion for a company Traverse an entity relationship graph List intel markets List FININT indicators List tagged emergent signals Cyber entity graph List CVE entities List threat actor entities --- # List CVE entities (/docs/api/reference/intel/listCVEsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Read the public intelligence change feed (/docs/api/reference/intel/listChangesV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List workspace company financial history (/docs/api/reference/intel/listCompanyFinancialObservationsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List intel entities (/docs/api/reference/intel/listEntitiesV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List FININT indicators (/docs/api/reference/intel/listFinintIntelV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List intel incidents (/docs/api/reference/intel/listIncidentsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List intel markets (/docs/api/reference/intel/listMarketsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List tagged emergent signals (/docs/api/reference/intel/listSignalsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List threat actor entities (/docs/api/reference/intel/listThreatActorsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # News (/docs/api/reference/news) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List news items List AI-generated recaps List available news feeds --- # List available news feeds (/docs/api/reference/news/listFeeds) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List news items (/docs/api/reference/news/listNews) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List AI-generated recaps (/docs/api/reference/news/listRecaps) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Append a ledger record (/docs/api/reference/ontology-ledger/appendLedgerRecordV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Export a ledger subject as STIX 2.1 (/docs/api/reference/ontology-ledger/getLedgerSubjectStixV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Fold a ledger subject (/docs/api/reference/ontology-ledger/getLedgerSubjectV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Ontology Ledger (/docs/api/reference/ontology-ledger) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List ledger subjects Open a ledger subject Fold a ledger subject Export a ledger subject as STIX 2.1 Export sealed ledger lines Append a ledger record Verify the ledger seal --- # Export sealed ledger lines (/docs/api/reference/ontology-ledger/listLedgerRecordsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List ledger subjects (/docs/api/reference/ontology-ledger/listLedgerSubjectsV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Open a ledger subject (/docs/api/reference/ontology-ledger/openLedgerSubjectV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Verify the ledger seal (/docs/api/reference/ontology-ledger/verifyLedgerV2) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # AI-generated intelligence brief (/docs/api/reference/osint/getCountryBrief) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Country instability index (/docs/api/reference/osint/getCountryCII) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Country-specific news (/docs/api/reference/osint/getCountryNews) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Prediction markets (/docs/api/reference/osint/getCountryPredictions) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Stock market index (/docs/api/reference/osint/getCountryStockIndex) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # OSINT (/docs/api/reference/osint) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List threat events List map events with coordinates Conflict Indicators Index AI-generated intelligence brief Stock market index Prediction markets Country-specific news Country instability index Military posture by theater Tracked military aircraft Naval vessel positions GPS interference zones Data source health List cyber threat indicators List prediction markets Y2 report-extracted events Search regional OSINT events Financial intelligence indicators --- # Tracked military aircraft (/docs/api/reference/osint/listAircraft) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Conflict Indicators Index (/docs/api/reference/osint/listCII) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List cyber threat indicators (/docs/api/reference/osint/listCyberThreats) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Financial intelligence indicators (/docs/api/reference/osint/listFinintIndicators) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # GPS interference zones (/docs/api/reference/osint/listGPSJamming) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Military posture by theater (/docs/api/reference/osint/listMilitaryPosture) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List threat events (/docs/api/reference/osint/listOsintEvents) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List map events with coordinates (/docs/api/reference/osint/listOsintMapEvents) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Search regional OSINT events (/docs/api/reference/osint/listOsintRegionalEvents) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # List prediction markets (/docs/api/reference/osint/listPredictionMarkets) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Data source health (/docs/api/reference/osint/listSourceStatus) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Naval vessel positions (/docs/api/reference/osint/listVessels) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Y2 report-extracted events (/docs/api/reference/osint/listY2Events) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # x402 receipt lookup (/docs/api/reference/payments/getX402Receipt) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Payments (/docs/api/reference/payments) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} x402 receipt lookup --- # Create an intelligence profile (/docs/api/reference/profiles/createProfile) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Delete a profile (/docs/api/reference/profiles/deleteProfile) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get a subscribed profile (/docs/api/reference/profiles/getProfile) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Profiles (/docs/api/reference/profiles) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List subscribed profiles Create an intelligence profile Get a subscribed profile Update a profile (partial) Delete a profile Update a profile (full replacement) --- # List subscribed profiles (/docs/api/reference/profiles/listProfiles) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Update a profile (partial) (/docs/api/reference/profiles/patchProfile) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Update a profile (full replacement) (/docs/api/reference/profiles/updateProfile) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Create a Project (/docs/api/reference/projects/createProject) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get a Project (/docs/api/reference/projects/getProject) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Projects (/docs/api/reference/projects) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List owned Projects Create a Project Get a Project Update, archive, or restore a Project --- # List owned Projects (/docs/api/reference/projects/listProjects) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Update, archive, or restore a Project (/docs/api/reference/projects/patchProject) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get a report (/docs/api/reference/reports/getReport) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get report audio (/docs/api/reference/reports/getReportAudio) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get TTS-preprocessed report text (/docs/api/reference/reports/getReportAudioText) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get report ontology graph (/docs/api/reference/reports/getReportGraph) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get report emergent signals (/docs/api/reference/reports/getReportSignals) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Get report as plain text (/docs/api/reference/reports/getReportText) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Reports (/docs/api/reference/reports) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List intelligence reports Get a report Get report emergent signals Get report ontology graph Get report audio Get report as plain text Get TTS-preprocessed report text --- # List intelligence reports (/docs/api/reference/reports/listReports) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Subscriptions (/docs/api/reference/subscriptions) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} Update subscription delivery method --- # Update subscription delivery method (/docs/api/reference/subscriptions/updateDelivery) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Create a webhook configuration (/docs/api/reference/webhooks/createWebhook) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Delete a webhook configuration (/docs/api/reference/webhooks/deleteWebhook) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Webhooks (/docs/api/reference/webhooks) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} List webhook configurations Create a webhook configuration Delete a webhook configuration Update a webhook configuration Test a webhook endpoint --- # List webhook configurations (/docs/api/reference/webhooks/listWebhooks) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Test a webhook endpoint (/docs/api/reference/webhooks/testWebhook) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Update a webhook configuration (/docs/api/reference/webhooks/updateWebhook) {/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */} export default function Layout(props) { const { APIPage, OpenAPIPage } = props.components ?? {}; // "APIPage" is the old name from v10, this allows both for backward compatibility const Comp = OpenAPIPage ?? APIPage; return ( <> {props.children} ); } --- # Retrieve Reports through the API (/docs/api/reports) Use the Reports API to retrieve published intelligence generated for profiles. Start with compact report metadata, then request only the representation or extracted artifact your integration needs. ## Choose a representation | Consumer | Representation | | --- | --- | | Agent | Markdown with cited sources | | Application | Compact or expanded JSON | | Analytics workflow | Signals or graph | | Human reader or listener | Plain text or audio | ## Required scopes | Operation | Bearer scope | | --- | --- | | List, detail, Markdown, plain text, signals, graph, and TTS-preprocessed text | `reports:read` | | Audio metadata or CDN redirect | `reports:audio` | The audio operation also depends on audio entitlement and an available recording. Every Report operation supports [x402 pay-per-request](/docs/api/x402) as an alternative to a bearer key. ## List reports For a bearer-key request without `profileId`, Y2 lists reports newest first across the active profiles subscribed in that key's user or workspace context. ```bash curl --get "https://api.y2.dev/api/v1/reports" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "limit=20" ``` To target one profile, pass its canonical `prf_...` ID: ```bash curl --get "https://api.y2.dev/api/v1/reports" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "profileId=$PROFILE_ID" \ --data-urlencode "limit=20" ``` The endpoint supports only `profileId`, `limit`, `cursor`, and `format`; it does not provide a date range filter. `limit` defaults to 20 and is capped at 100. An anonymous x402 caller has no subscription context, so `profileId` is required for `GET /reports`. Single-report and subresource calls identify the resource with `reportId`. JSON pages return `data`, `meta`, and `links`. Follow `links.next` until it is `null`. For NDJSON, send `format=ndjson` or `Accept: application/x-ndjson`; each line is one compact report and the next cursor is in `X-Y2-Next-Cursor`. ## Read compact report metadata ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID" \ --header "Authorization: Bearer $Y2_API_KEY" ``` The default response contains the canonical `rpt_...` and `prf_...` IDs, topic, summary, published timestamps, language, signal and graph counts, audio availability, profile identity, and links to each representation. It does not include the full report body or full source rows by default. An authenticated detail or subresource request must be subscribed to the report's profile. A missing report returns `404`; an authenticated key without that subscription returns `403`. ## Expand structured JSON Use the comma-separated `include` parameter to embed bounded related data. | Include | Added field | | --- | --- | | `content` | `content.markdown` and its media type | | `sources` | Canonical source records with `src_...` IDs and available attribution | | `signals` | Report-local emergent decision signals | | `graph` | Report-local ontology nodes, edges, incidents, and citations when available | | `audio` | Audio representation metadata or `null` | ```bash curl --get "https://api.y2.dev/api/v1/reports/$REPORT_ID" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "include=content,sources,signals" ``` `view=agent` is shorthand for `content,sources,signals`. It does not add the ontology graph or audio representation. ## Choose Markdown or plain text Request the canonical stored report representation: ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID" \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Accept: text/markdown" ``` `format=markdown` is equivalent. This returns the report body directly, not a JSON envelope. Request content with HTML stripped: ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/text" \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Accept: text/plain" ``` Omit `Accept: text/plain` to receive JSON with `text`, `wordCount`, and an estimated `readingTimeMinutes` at approximately 200 words per minute. ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/text" \ --header "Authorization: Bearer $Y2_API_KEY" ``` Use Markdown for grounded agents and publishing pipelines. Use `/text` when the consumer cannot render markup or needs reading-time metadata. ## Retrieve signals or the report graph The subresources avoid transferring the full report body: ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/signals" \ --header "Authorization: Bearer $Y2_API_KEY" curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/graph" \ --header "Authorization: Bearer $Y2_API_KEY" ``` Signals include decision fields, confidence, time horizon, normalized subjects, entity links when resolved, tags, and cited sources. Reports created before signal extraction return an empty `signals` array. The graph is a bounded snapshot extracted from that report, not a live traversal of the full Intel ontology. It contains report-local nodes, relationships, incident anchors, citations, and counts. Reports created before graph extraction return `graph: null` and zero counts. ## Retrieve narration or TTS text Request audio metadata with `reports:audio`: ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/audio" \ --header "Authorization: Bearer $Y2_API_KEY" ``` The JSON body contains the MP3 URL, duration, formatted duration, media type, and file size when known. To redirect directly to the CDN asset, pass `redirect=true` or send an `Accept` header that contains an audio media type. The redirect is `302`, and the redirect response can be cached for 24 hours. A report without audio returns `404 NO_AUDIO`. `/audio-text` requires `reports:read`, not `reports:audio`. It returns the exact preprocessing path used for narration, including pronunciation normalization and applicable profile branding: ```bash curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/audio-text" \ --header "Authorization: Bearer $Y2_API_KEY" ``` The duration fields on this endpoint are estimates based on character count; they are not measured audio duration. Reports, summaries, signals, and ontology graphs are synthesized intelligence. Preserve source URLs and confidence fields, and verify high-impact conclusions against primary evidence. --- # Configure Report Webhooks (/docs/api/webhooks) Use a webhook when your application should react as soon as Y2 publishes a report. This guide covers configuration and consumption through the public API. For the delivery product overview, see [Webhook delivery](/docs/delivery/webhooks). ```mermaid flowchart LR A[Create whk_ config] --> B[Test endpoint] B --> C[Attach to sub_ subscription] C --> D[Receive CloudEvent] D --> E[Verify signature] E --> F[Deduplicate by event ID] F --> G[Fetch rpt_ representations] ``` ## Before you begin You need: - a paid workspace plan with webhook delivery; - an API key with `webhooks:manage`; - a public HTTPS endpoint that accepts `POST` requests; and - a secret generated and stored by your application if you want signed deliveries. Y2 rejects loopback, private, link-local, cloud metadata, multicast, and other reserved network destinations. In production, webhook URLs must use HTTPS. Custom headers must have string values; `Host`, `Content-Length`, and `Content-Type` cannot be configured. ## 1. Create a webhook configuration Send a stable `Idempotency-Key` when a client may retry the request: ```bash curl --request POST "https://api.y2.dev/api/v1/webhooks" \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Content-Type: application/json" \ --header "Idempotency-Key: create-primary-webhook-01" \ --data '{ "name": "Primary report receiver", "url": "https://example.com/webhooks/y2", "secret": "replace-with-a-random-secret", "headers": { "X-Integration": "intelligence-pipeline" } }' ``` A successful request returns `201`, a canonical `whk_...` ID, `Location`, and `ETag`. Store the ID; you need it to test the endpoint and attach it to a subscription. List and mutation responses reveal whether signing is configured and return custom header names, but never return the secret or custom header values. ## 2. Test the receiver ```bash curl --request POST "https://api.y2.dev/api/v1/webhooks/$WEBHOOK_ID/test" \ --header "Authorization: Bearer $Y2_API_KEY" ``` The test request is a CloudEvents 1.0 event with type `dev.y2.webhook.test.v1`. Y2 waits up to 10 seconds. A `2xx` response from your receiver produces `200`; a timeout, network error, or non-`2xx` response produces `422`. The test verifies connectivity and signing, but it does not attach the configuration to a report subscription. ## 3. Attach the webhook to a subscription Use the subscription's canonical `sub_...` ID, not its profile ID: ```bash curl --request PATCH \ "https://api.y2.dev/api/v1/subscriptions/$SUBSCRIPTION_ID/delivery" \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Content-Type: application/json" \ --data "{\"deliveryMethod\":\"webhook\",\"webhookConfigId\":\"$WEBHOOK_ID\"}" ``` The webhook must be active and belong to the same user or workspace scope as the subscription. Changing a profile does not change its subscription delivery settings. ## 4. Process the report event Production deliveries use structured CloudEvents with media type `application/cloudevents+json` and event type `dev.y2.report.generated.v1`: ```json { "specversion": "1.0", "id": "y2mexamplelogicalevent01", "source": "https://api.y2.dev/api/v1/profiles/prf_0123456789abcdef01234567", "type": "dev.y2.report.generated.v1", "subject": "reports/rpt_0123456789abcdef01234567", "time": "2026-07-21T18:00:00.000Z", "datacontenttype": "application/json", "dataschema": "https://api.y2.dev/schemas/events/report-generated-v1.json", "data": { "report": { "id": "rpt_0123456789abcdef01234567", "profileId": "prf_0123456789abcdef01234567", "summary": "A concise report summary.", "status": "published", "generatedAt": "2026-07-21T17:59:42.000Z", "language": "en", "intelligence": { "signalCount": 3, "graphNodeCount": 12 }, "audio": { "status": "available", "durationSeconds": 284 } }, "subscription": { "id": "sub_0123456789abcdef01234567" }, "links": { "report": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567", "markdown": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?format=markdown", "sources": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?include=sources", "signals": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/signals", "graph": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/graph", "audio": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/audio" } } } ``` The event is intentionally compact. Use the supplied links and a key with the relevant Reports scope to retrieve full Markdown, sources, signals, graph, or audio. ## Delivery headers | Header | Meaning | | --- | --- | | `Content-Type` | `application/cloudevents+json` | | `User-Agent` | `Y2-Webhook-Delivery/3.0` | | `X-Y2-Timestamp` | Attempt time as Unix seconds | | `X-Y2-Event-Id` | Stable logical event ID; matches the body `id` | | `X-Y2-Attempt-Id` | Unique transport-attempt ID | | `X-Y2-Attempt` | One-based attempt number | | `Idempotency-Key` | Stable logical event ID | | `X-Y2-Signature` | `sha256=` when a secret is configured | Configured custom headers are sent first. Y2's delivery headers take precedence if names collide. ## Verify the signature Compute HMAC-SHA256 over the exact raw request bytes. Do not parse and reserialize the JSON before verification. ```typescript import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyY2Webhook( rawBody: Buffer, signatureHeader: string | null, secret: string, ): boolean { if (!signatureHeader) return false; const expected = Buffer.from( `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`, ); const actual = Buffer.from(signatureHeader); return actual.length === expected.length && timingSafeEqual(actual, expected); } ``` Also compare `X-Y2-Timestamp` with your current time and reject values outside your replay window; five minutes is a practical default. Only return `2xx` after the event has been durably accepted. ## Handle attempts and failures The event body and `X-Y2-Event-Id` remain stable if the same logical delivery is attempted again. `X-Y2-Attempt-Id` and `X-Y2-Attempt` identify the transport attempt. Deduplicate on the body `id`, `X-Y2-Event-Id`, or `Idempotency-Key` before starting downstream work. Each event has at most five automatic attempts, with a 10-second timeout and delays of 5 seconds, 30 seconds, 5 minutes, and 30 minutes. Network errors, timeouts, HTTP 408/429, and 5xx responses are retried. Other HTTP rejections pause the destination immediately. Five consecutive failures across a destination also pause it; successful delivery resets that counter. Retries reuse the original payload and event ID and do not restart report generation. The configuration owner receives one warning email on the first temporary failure and one email when delivery is paused. Paused destinations receive no further automatic attempts. Reports, configuration, and delivery history remain available; other destinations continue independently. `GET /webhooks` exposes status and delivery health, including the failure reason/status, last success, suspension time, and last successful test. To recover: 1. Save repaired configuration with `PUT`, keeping `isActive: false`. 2. Call `POST /webhooks/{webhookId}/test`; disabled destinations can be tested. A successful response includes the configuration's new `ETag`, because testing updates its delivery health. 3. Within 15 minutes, `PUT` the same configuration with `isActive: true`, using the test response's `ETag` in `If-Match`. Include the same secret and headers: changing the endpoint, signing secret, or headers invalidates the test. 4. In **Delivery history and replay** in webhook settings, explicitly replay retained failed events. Enabling delivery does not automatically replay the backlog. Enabling without a current successful test returns `409 WEBHOOK_TEST_REQUIRED`. A test failure does not consume the production retry budget. ## Replace or delete a configuration `PUT /webhooks/{webhookId}` replaces the complete mutable configuration. `name` and `url` are required. Omitted `secret` and `headers` are cleared; omitted `isActive` preserves the current status. Send the latest returned `ETag` in `If-Match` when you have one to prevent overwriting a concurrent change. Before deleting a webhook, move every attached subscription to another delivery method or webhook. Deletion returns `409 WEBHOOK_IN_USE` while any subscription still references the configuration; a successful deletion returns `204` with no body. --- # Explore the API with Workbench (/docs/api/workbench) The API Workbench turns Y2's live OpenAPI description into a browsable endpoint catalog, parameter editor, request runner, and snippet generator. Open **[Developers → API Workbench](/app/developers/api-workbench)** in the dashboard. New API access is available on Pro and Elite. Existing grandfathered Lite API users retain Workbench access during the grace period. ## What Workbench reads from OpenAPI The page loads `/api/openapi.yaml` and uses the current contract to show: - endpoint categories, methods, paths, summaries, and operation IDs; - required bearer scopes from `x-required-scopes`; - path and query parameter descriptions, types, enums, examples, and defaults; and - operation- or path-level server overrides, including the Intel v2 base URL. Parameters use enum selectors, boolean toggles, and numeric or text inputs where applicable. Required path and query values are checked before a live request leaves the browser. The current Workbench editor does not render OpenAPI request-body fields. Use it directly for `GET`, bodyless `POST`, and other path/query-driven operations. For profile creation, webhook configuration, delivery updates, and other JSON-body mutations, start from the generated endpoint reference or the relevant API guide. ## Run a read request ### Paste an API key Enter a key in the password field at the top of the page. The value remains in React component state and is added as `Authorization: Bearer ...` only when you run a request. ### Open a category and endpoint Categories begin collapsed. Open one such as **Intel**, **OSINT**, **Reports**, or **News**, then choose an endpoint. The row shows its method, path, scope, parameter counts, and operation ID. ### Fill required values Path parameters are always required. Required query parameters are shown separately; enable only the optional query parameters you want to send. ### Run the request Select **Run request**. Workbench calls the canonical production API from your browser and shows the response status and parsed body inline. JSON is formatted; non-JSON bodies are shown as text. If a required path or query value is missing, Workbench produces a local `400` explanation instead of sending an incomplete request. A missing key produces a local `401` prompt. Requests use the workspace associated with the pasted key. Switching your active workspace in the app does not change the key's workspace. ## Generate a copy-safe snippet Each expanded endpoint provides **curl**, **TypeScript**, and **Python** tabs. The generated code: - references `$Y2_API_KEY`, `process.env.Y2_API_KEY`, or `os.environ["Y2_API_KEY"]`; - never includes the key pasted into Workbench; - substitutes an unset required path or query value with an `UPPER_SNAKE_CASE` environment variable; and - adds comments for the required and currently enabled query parameters, including available descriptions, types, enums, and defaults. For example, after enabling `limit` without entering a value, a curl snippet can contain: ```bash # Required: # countryCode (path) — ISO 3166-1 alpha-2 country code # # Optional: # limit (query · integer · default: 8) — Maximum results curl "https://api.y2.dev/api/v1/osint/countries/$COUNTRY_CODE/news?limit=$LIMIT" \ -H "Authorization: Bearer $Y2_API_KEY" \ -H "Accept: application/json" ``` Set the placeholders before running the copied command: ```bash export Y2_API_KEY="y2_..." export COUNTRY_CODE="UA" export LIMIT="8" ``` Best for one-off checks and reproducing a request in a bug report. Unset values use shell variables such as `$COUNTRY_CODE`. Uses native `fetch` and reads secrets or missing values from `process.env`. Move the API key to server-side configuration before integrating the snippet into an application. Uses `httpx`, reads configuration from `os.environ`, calls `raise_for_status()`, and applies a 30-second client timeout. ## Use the AI Quickstart prompt **Copy prompt** copies a short instruction that directs a coding agent to the canonical Agentic DX and MCP guides before it integrates Y2. It contains documentation URLs and safety guidance, not the API key entered on the page. Use this prompt as orientation, then give the agent the exact endpoint, scope, and outcome you need. Keep `Y2_API_KEY` in the agent runtime or MCP client environment—not in the prompt or tool arguments. ## Know the current boundaries | Capability | Current behavior | | --- | --- | | API target | Production server from OpenAPI; falls back to `https://api.y2.dev/api/v1` | | Path and query fields | Interactive and OpenAPI-driven | | Request body fields | Not rendered; use endpoint reference examples | | Live response | Status and response body | | Response headers | Not displayed in the inline pane | | Authentication | Bearer key required by the runner | | x402 | Use curl or an x402 client outside Workbench | | Saved state | Key, expanded rows, and parameter values are page component state, not durable presets | Workbench is an integration aid, not a replacement for application-level error handling. Before shipping copied code, add timeouts, typed response handling, pagination, retry policy, and secret management appropriate to your runtime. --- # Pay per Request with x402 (/docs/api/x402) x402 lets an agent or application pay for a supported Y2 read request without creating an account or API key. Y2 advertises an x402 v2 payment requirement, verifies and settles the signed payment, then serves the normal endpoint response. ```mermaid sequenceDiagram participant C as Client participant Y as Y2 API participant F as Facilitator C->>Y: Request without Authorization Y-->>C: 402 + PAYMENT-REQUIRED C->>C: Validate and sign requirement C->>Y: Same request + PAYMENT-SIGNATURE Y->>F: Verify payment F-->>Y: Valid Y->>F: Settle payment F-->>Y: Transaction Y->>Y: Run resource handler Y-->>C: Resource response + PAYMENT-RESPONSE ``` ## Choose bearer auth or x402 | Mode | Use it for | | --- | --- | | Bearer API key | Recurring integrations, workspace-scoped data, writes, and plan rate limits | | x402 | Anonymous, one-off, agent-driven access to supported public read operations | A request with `Authorization: Bearer ...` always takes the API-key path. An invalid bearer token returns its normal `401` or `403`; Y2 does not silently fall back to x402. Omit `Authorization` entirely when you intend to pay per request. ## Supported API surfaces x402 is enabled on registered read operations in these families: - Reports, including Markdown, text, signals, graph, audio, and TTS text; - News items, recaps, and feed discovery; - OSINT feeds and country intelligence; - Intel v2 incidents, entities, markets, financial intelligence, signals, cyber, and changes. It is not an alternative for API-key-only surfaces such as profile management, webhook configuration, or Agent Y2. Check an operation's `security` and `x-x402` fields in the [generated endpoint reference](/docs/api/reference) before building a payment flow. Anonymous callers do not have a user or workspace context. For example, `GET /reports` requires a canonical `profileId` query parameter on the x402 path. ## Read the payment challenge Send the exact resource request without a bearer key: ```bash curl --include \ "https://api.y2.dev/api/v1/news?topics=markets&limit=5" ``` The `402` response includes a base64-encoded `PAYMENT-REQUIRED` header and the decoded requirement as JSON. Its shape is similar to: ```json { "x402Version": 2, "error": "Payment required", "resource": { "url": "https://api.y2.dev/api/v1/news?topics=markets&limit=5", "description": "Real-time direct-source news terminal items", "mimeType": "application/json" }, "accepts": [ { "scheme": "exact", "network": "eip155:8453", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "2000", "payTo": "0x...", "maxTimeoutSeconds": 60, "extra": { "name": "USD Coin", "version": "2" } } ], "extensions": { "bazaar": { "discoverable": true } } } ``` Do not hard-code `asset`, `payTo`, EIP-712 domain fields, or the amount from this example. Read and validate the returned requirement every time. `amount` is expressed in atomic token units; USDC uses six decimals, so `"2000"` represents `$0.002`. Production requirements use Base mainnet (`eip155:8453`). Development deployments can use Base Sepolia (`eip155:84532`). The network is a CAIP-2 identifier, not the legacy `base` slug. ## Make a paid request in TypeScript The following flow matches the x402 packages used by the current Y2 implementation: ```bash npm install @x402/core@^2.18.0 @x402/evm@^2.18.0 viem@^2.55.2 ``` Create the signer in a server-side runtime or secure wallet environment. Never expose a raw private key in browser code. ```typescript import { x402Client } from "@x402/core/client"; import { x402HTTPClient } from "@x402/core/http"; import { ExactEvmScheme } from "@x402/evm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; const privateKey = process.env.X402_PRIVATE_KEY as `0x${string}` | undefined; if (!privateKey) throw new Error("X402_PRIVATE_KEY is required"); const signer = privateKeyToAccount(privateKey); const core = new x402Client().register("eip155:*", new ExactEvmScheme(signer)); const x402 = new x402HTTPClient(core); const url = "https://api.y2.dev/api/v1/news?topics=markets&limit=5"; const challengeResponse = await fetch(url, { headers: { Accept: "application/json" } }); if (challengeResponse.status !== 402) { throw new Error(`Expected an x402 challenge, received ${challengeResponse.status}`); } const challengeBody = await challengeResponse.json(); const requirement = x402.getPaymentRequiredResponse( (name) => challengeResponse.headers.get(name), challengeBody, ); const option = requirement.accepts[0]; if (!option || option.scheme !== "exact" || option.network !== "eip155:8453") { throw new Error("Y2 returned an unsupported payment option"); } const maxAtomicUsdc = 2_000n; if (BigInt(option.amount) > maxAtomicUsdc) { throw new Error("Payment exceeds the client price ceiling"); } const payment = await x402.createPaymentPayload(requirement); const paidResponse = await fetch(url, { headers: { Accept: "application/json", ...x402.encodePaymentSignatureHeader(payment), }, }); const settlement = paidResponse.headers.has("PAYMENT-RESPONSE") ? x402.getPaymentSettleResponse((name) => paidResponse.headers.get(name)) : null; const result = await paidResponse.json(); console.log({ status: paidResponse.status, settlement, result }); ``` The price ceiling above is specific to the `$0.002` News example. Set a ceiling appropriate to the operation you selected. Keep the first and second request URL, method, query, and body identical. ## Understand the headers | Header | Direction | Meaning | | --- | --- | --- | | `PAYMENT-REQUIRED` | Response | Base64-encoded x402 v2 challenge | | `PAYMENT-SIGNATURE` | Request | Base64-encoded signed payment payload | | `PAYMENT-RESPONSE` | Response | Base64-encoded settlement result | | `Authorization` | Request | Selects bearer authentication when present | `X-PAYMENT` and `X-PAYMENT-RESPONSE` are legacy x402 v1 names. New Y2 clients should use the v2 headers above. ## Prices and wallet limits Each operation maps to one current tier. The generated endpoint page is the source of truth for the operation's exact price. | Tier | Price | Per wallet/minute | Per wallet/day | | --- | ---: | ---: | ---: | | Health/meta | $0.001 | 60 | 10,000 | | Standard read | $0.002 | 60 | 5,000 | | Enriched data | $0.01 | 30 | 2,000 | | Premium report list | $0.05 | 10 | 500 | | Premium single report | $0.25 | 10 | 200 | | Premium report audio | $0.50 | 5 | 100 | Y2 also applies a global per-endpoint circuit breaker. A wallet limit returns `429`; an endpoint capacity limit returns `503`. Both are checked before settlement, so the rejected request is not charged. ## Store and inspect a receipt The EIP-3009 authorization inside the signed payment contains a nonce. Treat that nonce as a receipt lookup capability and store it with the request result: ```bash curl "https://api.y2.dev/api/v1/x402/receipts/$PAYMENT_NONCE" ``` The lookup is public to anyone who knows the nonce. It returns the payer address, endpoint, USD amount, network, status, and—when available—the transaction hash and settlement time. It does not return the signature, token authorization, pay-to address, or full payment payload. | Receipt status | Meaning | | --- | --- | | `verify_failed` | The facilitator rejected the payment | | `rate_limited` | Wallet or endpoint limits blocked the request before settlement | | `replay_blocked` | The payer and nonce combination was already recorded | | `settle_failed` | Verification passed but settlement did not complete | | `settled` | Payment settled and Y2 invoked the resource handler | | `handler_failed` | Payment settled, then the resource handler threw | A settled payment can still be followed by an application response such as `400`, `404`, or a temporary data-readiness error. Validate resource IDs and parameters before signing, inspect the paid response status and body, and retain the nonce and `PAYMENT-RESPONSE` metadata for support. ## Handle failures | Status | Typical cause | | --- | --- | | `401` or `403` | Invalid bearer key or missing bearer scope; no x402 fallback | | `402` | Missing or invalid payment, unsupported payment payload, or nonce replay | | `429` | Per-wallet minute or daily limit exceeded | | `503` | x402 unavailable, settlement infrastructure unavailable, or endpoint circuit breaker open | | Other endpoint status | Payment settled, then the normal resource handler returned its own result | Never reuse an authorization nonce. When a result is ambiguous, check the receipt before creating a new payment so you do not confuse a settled request with a transport failure. --- # Desktop App (/docs/apps) Y2 Desktop puts your chats, projects, reports, and intelligence workspace in a dedicated app for Mac and Windows. The installer bundles the application and its runtime. You do not need Node.js, Bun, developer tools, or a separate browser installation to run it. The [Y2 download page](/download) offers **unofficial, unsigned beta** installers alongside release notes and checksums. They install as **Y2** and use your existing account and workspace. Apple notarization and Windows publisher signing are planned for a future release. If the page says **Downloads not published yet**, use [Y2 in your browser](/login). ## Requirements | Platform | Requirements | Installer | | --- | --- | --- | | Mac | macOS 12 or later; Apple silicon or Intel | Universal `.dmg` | | Windows | Windows 10 or later; 64-bit x64 processor | `.exe` setup wizard | An internet connection and a Y2 account are required. Desktop uses the same cloud service, workspace data, plan, and usage limits as the web app. It does not provide an offline copy of your workspace or run AI models on your device. No separate desktop subscription is required. ## Install on Mac 1. Open [Y2 downloads](/download) and select **Download for Mac** when available. 2. Open the downloaded `.dmg` file and drag **Y2** into **Applications**. 3. Eject the disk image, then open Y2 from Applications or Launchpad. 4. This beta is unsigned and not notarized. After macOS blocks the first launch, open **System Settings → Privacy & Security → Open Anyway** for Y2 if you choose to trust this download. Confirm **Open** when prompted. See [Apple's instructions](https://support.apple.com/102445). 5. Sign in with the email address or phone number you already use for Y2, then enter your verification code. New users can create their workspace through the same sign-in flow. ## Install on Windows 1. Open [Y2 downloads](/download) and select **Download for Windows** when available. 2. Open the downloaded `.exe` installer. An **Unknown publisher** warning is expected for this unsigned beta. If SmartScreen offers **More info → Run anyway**, you can use it after verifying the download's checksum. Smart App Control or organizational policies may block unsigned apps without an override; use [Y2 in your browser](/login) on those devices. 3. Follow the setup wizard to choose the installation location and complete installation. 4. Open **Y2** from the Start menu or desktop shortcut. 5. Sign in with your existing Y2 account, or complete the new-workspace setup. ## Verify a download The download page links the release notes and a versioned `SHA256SUMS.txt` file. To check file integrity, calculate the installer's SHA-256 digest and compare the full result with its entry in the checksum file. Replace the example path below with your downloaded file's path. On macOS, use Terminal: ```sh shasum -a 256 "/path/to/Y2-installer.dmg" ``` On Windows, use PowerShell: ```powershell Get-FileHash "C:\path\to\Y2-installer.exe" -Algorithm SHA256 ``` If the digest differs, discard the file and download it again from [Y2 downloads](/download). Checksums verify file integrity. These unofficial builds do not have a verified publisher signature. ## Use your workspace Your saved chats, projects, intelligence profiles, and reports are shared with the web app. Changes synchronize through Y2's cloud service while you are online. Select the same workspace on each device to see the same information. Feature availability follows your [workspace plan](/docs/plans). Billing opens securely in your default browser. After checkout or managing a subscription, choose **Open Y2 Desktop** on the return page. If your browser does not open the app, reopen Y2 directly and check **Settings → Billing**. Billing status may take a moment to refresh. For research and account workflows, the rest of the [Y2 documentation](/docs) applies to desktop as well. ## Sign-in on your device Y2 connects directly to its cloud API using your normal sign-in. You do not need a developer account or a deployment key. The desktop app saves sign-in credentials with your operating system's encryption so you can reopen it without signing in each time. On Mac, this can prompt for access to Y2's Keychain item. If secure storage is locked or access is denied, unlock it and choose **Try again** in Y2. **Clear saved sign-in** removes the local credentials so you can sign in again. Signing out also removes the saved credentials; your cloud workspace remains available. ## Updates These unofficial builds use **manual updates**. Download the latest installer from [Y2 downloads](/download), quit Y2, and install the new version. Your account and cloud workspace remain the same. The app may display its internal channel as **local**; automatic updates are not configured for this build. Signed automatic updates are planned for a future release. If Y2 requires a newer version to connect to the service, follow the update prompt or download the current release manually. You can continue in the web app while resolving an installation or update problem. ## Installation troubleshooting | Problem | What to do | | --- | --- | | No download button | The public release is still being prepared. Use the web app and check the download page for availability. | | macOS cannot verify the developer | Follow the Open Anyway steps above for this unsigned beta. If macOS reports damage or malware, compare the checksum and contact support; do not disable Gatekeeper globally. | | Windows reports an unknown publisher | This is expected for the unsigned beta. Verify the checksum and use the per-app SmartScreen exception if available. If device policy or Smart App Control blocks it, use the browser app. | | The app opens but cannot connect | Check your internet connection and whether Y2 opens in your browser. A work network may need to allow Y2's service and download endpoints. | | A page fails to load | Use the recovery screen to retry or restart Y2. If it continues, use the web app and report the app version and operating-system version. | | An update fails | Check your connection and available disk space, then retry. You can install the latest release from the official download page without creating a new account. | | Workspace data looks different | Check the signed-in account and selected workspace on both devices. Desktop has the same workspace permissions as the web app. | For help, email [support@y2.dev](mailto:support@y2.dev). Include your operating-system version, Y2 version and channel, and the exact error message. Do not send passwords or verification codes. ## Uninstall Quit Y2 before uninstalling. On macOS, move the application from Applications to Trash. On Windows, uninstall Y2 from **Settings → Apps**. Removing the desktop application does not delete your cloud workspace or cancel your subscription. Manage your account and billing in Y2. Check the current Mac and Windows release and its installation files. Continue with your existing account and cloud workspace. --- # Audio Reports (/docs/audio) Audio Reports turn a generated intelligence report into an MP3 narration. The feature is available in active Pro and Elite workspaces and is currently labeled **Alpha** in the profile editor. Audio is configured per profile. Enabling narration affects future report runs; it does not generate audio for reports that already exist. ## Enable narration for a profile ### Open the profile editor Go to **InfoOps → My Profiles**, then create a profile or edit an existing one. ### Open Advanced Continue to the **Advanced Configuration** step and find **Audio Narration**. ### Turn on audio Enable the switch. Free and Lite workspaces cannot enable it; the backend also rejects an enabled audio configuration when the active workspace lacks the entitlement. ### Choose the narration settings Preview and select a voice, choose a generation speed, and optionally assign a synced pronunciation dictionary. If no voice is selected, Y2 uses its default professional narrator. ### Save the profile Audio is generated the next time that profile produces a non-degraded report. The voice library groups available voices into News & Broadcast, Professional, Narrative & Documentary, and Casual & Conversational. The stored catalog can include English and Spanish voices with masculine, feminine, or neutral labels. Availability is data-driven, so the exact voice list can change. The current dictionary selector lists all dictionaries in the active scope; it does not filter them to the selected voice's language. Check the language badge yourself before assigning one. ## What happens during generation Y2 prepares a speech-specific version of the report before calling the text-to-speech provider: - It removes the trailing references section, citation markers, HTML, and Markdown formatting. - It normalizes headings, punctuation, common entities, numbers, currency, acronyms, and tickers for spoken output. - It adds the assigned branding template's audio intro, before-report audio block, after-report audio block, and outro. When no value is configured, Y2 branding defaults apply. - It applies the assigned pronunciation dictionary only when that dictionary has a provider ID and a `synced` status. - It generates an MP3 with Cartesia Sonic-3 at 44.1 kHz and 128 kbps, then stores the file and its metadata in Convex. ```mermaid flowchart LR A[Generate report] --> B[Prepare speech text] B --> C[Apply branding and synced dictionary] C --> D[Cartesia Sonic-3] D --> E[MP3 in Convex storage] E --> F[In-app player] E --> G[Email link] E --> H[API and webhook status] ``` Audio output is limited to 20 MB. Generation is also skipped when its estimated cost would push the report above a configured per-report budget. Audio is an optional workflow step. A missing API key, provider error, oversize result, budget limit, or degraded report can leave the report without audio. Report delivery continues when the audio step returns no file. The stored duration is an estimate based on the prepared text, not a measurement read from the finished MP3. The in-app player replaces it with the browser-reported media duration after the file loads. ## Create a pronunciation dictionary Pronunciation dictionaries are also an Alpha feature. Pro workspaces can store up to 10 and Elite workspaces up to 100. Each dictionary can contain up to 200 entries. ### Open Audio Go to **InfoOps → Audio** and select **Create** under Pronunciation Dictionaries. ### Define the dictionary Enter a name, optional description, and English or Spanish language. Add entries individually or use the CSV bulk-import control. ### Add replacements Each entry stores a source word, a pronunciation value, and an Alias or IPA label. The current provider sync sends entries as text-to-alias replacements, regardless of the stored label, so use a readable replacement string and test the generated result. ### Save and verify sync Creating or editing a dictionary starts an automatic provider sync. Confirm that its badge becomes **Synced**. If it becomes **Error**, inspect the displayed error and use the refresh button to retry after correcting the entries. ### Assign it to a profile Return to the profile's **Advanced Configuration**, enable Audio Narration, select the dictionary, and save. | Constraint | Enforced limit | | --- | ---: | | Dictionaries in Pro | 10 | | Dictionaries in Elite | 100 | | Entries per dictionary | 200 | | Dictionary name | 50 characters | | Description | 200 characters | | Source word | 100 characters | | Replacement | 300 characters | Editing entries moves the dictionary back to `pending` before it is synced again. If a selected dictionary is pending, errored, deleted, or outside the profile's tenant scope, Y2 does not apply it during generation. ## Customize spoken branding The **InfoOps → Audio** page also exposes the intro and outro for each branding template in the active workspace. Use `{name}` to insert the profile name. | Spoken content | Limit | Position | | --- | ---: | --- | | Audio intro | 100 characters | Before all other narration | | Before-report audio | 200 characters | After the intro and optional label | | After-report audio | 200 characters | Before the outro and optional label | | Audio outro | 200 characters | At the end | The before- and after-report audio fields live in the full [Branding Templates editor](/docs/profiles/branding). A template must be explicitly assigned to the profile for its custom values to apply. ## Play and deliver audio When audio is available, the report page displays **Listen**. The bottom-sheet player supports play and pause, seeking, download, and playback rates of 0.75x, 1x, 1.25x, 1.5x, and 2x. Voice and duration metadata are displayed when available. Delivery surfaces expose audio differently: | Surface | Current behavior | | --- | --- | | In-app report | Shows the player only when the report has an audio URL | | Email | Shows an “Audio Narration Available” callout linking to the report page; the MP3 is not attached | | Webhook | Reports `available` or `unavailable`, an estimated duration, and an API audio link | | Reports API | Returns audio metadata or redirects to the stored MP3 URL | Audio files are removed when their owning report, profile, workspace, or account is deleted and can also be affected by storage cleanup. Do not treat a returned storage URL as a permanent archive. ## Retrieve audio through the API Use an API key with the `reports:audio` scope, or complete the endpoint's x402 flow. The path uses the report's public API ID. ```bash curl --request GET \ --url "https://api.y2.dev/api/v1/reports/REPORT_ID/audio" \ --header "Authorization: Bearer YOUR_API_KEY" ``` ```json { "data": { "url": "https://example.convex.cloud/api/storage/…", "duration": 847, "durationSeconds": 847, "durationFormatted": "00:14:07", "format": "mp3", "mimeType": "audio/mpeg", "fileSizeBytes": 13420800 } } ``` `duration` is retained for compatibility and is deprecated; use `durationSeconds`. Add `?redirect=true`, or request an audio media type with the `Accept` header, for a `302` response to the stored file URL. The related `GET /api/v1/reports/{reportId}/audio-text` endpoint requires `reports:read` and returns the speech-preprocessed text, character count, and estimated duration without requiring that an MP3 already exists. See the generated [Reports reference](/docs/api/reference/reports) for canonical schemas, errors, rate limits, and x402 pricing. ## Troubleshoot audio Confirm that the active workspace—not another personal or team workspace—is Pro or Elite. Free and Lite workspaces do not have the audio entitlement. The button appears only after audio metadata is stored. Confirm that narration was enabled before the run. Provider configuration, a degraded report, the 20 MB limit, or a per-report budget can cause audio to be skipped without blocking report delivery. Confirm that the profile has the intended dictionary selected and that the dictionary badge says Synced. Check the dictionary and voice language manually because the current selector does not enforce a match. Generation stores an estimate based on text length. The player reads the MP3's actual duration from browser media metadata after loading, so the value can change. Verify the public report ID, report access, and that audio exists for the report. The endpoint uses the same not-found response when no audio is available. ## Next steps Configure the report workflow that produces narration. Add intros, outros, and channel-specific static content. Retrieve reports and their derived representations programmatically. Compare audio access and pronunciation-dictionary quotas. --- # Create an Automation (/docs/automations/creating) This tutorial creates an Automation, activates it, and verifies its first run. ## Before you begin You need a Pro or Elite active workspace and a write-capable workspace role. If the Automation will use a Project or profile trigger, create that resource in the same workspace first. ## 1. Open Automations Open **Automations** from the app navigation. If you arrived from a Project, the form can start with that Project selected. ## 2. Define the work Select **New Automation**, enter a name, and write the instruction Agent Y2 should execute. Keep the instruction focused on one repeatable outcome. The instruction can contain up to 8,000 characters. ## 3. Choose the context Optionally select a Project. Y2 accepts only an active Project owned by you in the active workspace. Leaving Project blank creates a workspace-scoped output thread. ## 4. Choose a trigger For a schedule, choose daily, weekly, or monthly, then set the local time and timezone. Weekly schedules also require a weekday; monthly schedules require a day from 1 through 28. For a report trigger, select an active InfoOps profile from the same owner and workspace. The Automation runs after a completed report event is accepted. ## 5. Choose notifications and status Choose notifications for all runs, failures only, or none. Save as **Draft** while reviewing the definition, or save as **Active** to accept triggers. Active schedule definitions receive their next scheduled run. ## 6. Verify the Automation Use **Run now** on an active definition. Review run history for queued, running, succeeded, failed, skipped, or canceled status, then open the output thread when it is available. Y2 skips overlapping runs, stale definition revisions, lost plan or membership access, and inaccessible Projects. A skipped run does not bypass workspace or owner checks. --- # Automations (/docs/automations) Automations run a bounded Agent Y2 instruction without requiring someone to open Chat. An Automation can run on a schedule or after a selected InfoOps profile finishes a report, and it can optionally keep its output inside a Project. Automations are not included with Free or Lite. Pro workspaces can keep up to 10 active, paused, or draft definitions; Elite workspaces can keep up to 100. The limit is shared by the workspace. ## Definition and run separation The Automation definition stores its name, instruction, trigger, notification policy, optional Project, and current revision. Every execution creates a separate run snapshot. Editing a definition does not rewrite prior run history. Only one run for a definition can execute at a time. When a trigger arrives while another run is active, the newer run is skipped instead of overlapping. ## Trigger types | Trigger | Behavior | |---|---| | Schedule | Daily, weekly, or monthly in the selected IANA timezone. | | Profile report completed | Runs once for each accepted completion event from the selected active profile. | | Manual | **Run now** creates an immediate run from an active definition. | Profile triggers must reference a profile owned by the same user in the same workspace. A Project-bound Automation must also reference an active Project with the same owner and workspace. ```mermaid flowchart LR trigger[Schedule or report event] --> admission[Workspace and entitlement check] admission --> run[Agent Y2 run] run --> thread[Workspace or Project output thread] run --> history[Run history and notification] ``` ## Access changes and downgrade safety Y2 checks current workspace membership, plan access, definition revision, and Project access again when a run starts. If Automations are no longer available, Y2 skips the run, pauses the definition, and cancels its schedule. A downgraded owner can still pause or archive an existing definition so cleanup is not blocked by the upgrade gate. ## Cost and output Each accepted run reserves a bounded Agent Y2 budget, then settles actual model and tool costs or refunds the reservation after a failed or skipped run. Output is written to the definition's workspace-bound thread. Project-bound output stays in that Project. ## Elite governance Elite audit logs bind Automation creation, edits, status changes, and manual run requests to the acting user and workspace. Run records also retain the owner, workspace, trigger, definition revision, billing state, and output identifiers. ## Choose the next task Configure a trigger, instruction, Project, and notification policy. Review limits, statuses, triggers, run states, and access checks. Build the private objective and context an Automation can operate within. --- # Automations Reference (/docs/automations/reference) ## Availability and limits | Plan | Access | Shared workspace definition limit | |---|---|---:| | Free | No | 0 | | Lite | No | 0 | | Pro | Yes | 10 | | Elite | Yes | 100 | Draft, active, and paused definitions count toward the limit. Archived definitions do not. Definitions are owner-private even in an Elite organization. ## Definition fields | Field | Constraint or value | |---|---| | Name | Required, up to 100 characters. | | Instructions | Required, up to 8,000 characters. | | Operation | `agent_y2` | | Overlap policy | `skip_if_running` | | Status | Draft, active, paused, or archived. | | Notification policy | All runs, failures only, or none. | | Project | Optional active Project with the same owner and workspace. | ## Schedule triggers | Frequency | Required fields | |---|---| | Daily | Time of day and IANA timezone. | | Weekly | Time of day, timezone, and weekday. | | Monthly | Time of day, timezone, and day of month. | ## Run states `queued` → `running` → `succeeded` or `failed` is the normal path. A run can instead become `skipped` when admission checks fail or another run overlaps, or `canceled` when its work is explicitly stopped. Billing states are `reserved`, `settled`, `released`, and `none`. A released state means the request hold was returned without creating settled usage. Each run snapshots the definition revision, instruction, name, trigger, workspace, optional Project, and cost metadata. ## Live admission checks Before paid work starts, Y2 verifies: - The definition is active and still at the queued revision. - The owner is still a member of the definition workspace. - The workspace still has Pro or Elite Automation access and Chat capacity. - A bound Project is active, owner-matched, workspace-matched, and entitled. - No earlier run for the definition is currently active. These checks apply to schedules, report events, manual runs, and workflow continuation. --- # March 2026 Release Archive (/docs/changelog/2026-03) [← Back to the latest release](/docs/changelog) ## March 2026: Y2SOURCE Event Provenance Engine This entry describes the March 2026 launch. At launch, Y2SOURCE was available on **Lite** and **Pro**. See [Plans and limits](/docs/plans) and the [OSINT API guide](/docs/api/osint) for current access and request behavior. Y2 intelligence reports now feed the Situation Room. The **Y2SOURCE extraction pipeline** extracts structured geopolitical events from reports generated by the Y2 Orchestration Engine. Each event includes provenance metadata linking it to the source report. ### What changed #### Event extraction from reports - Every Y2 intelligence report is now processed through a structured extraction pipeline that identifies discrete geopolitical events, including conflicts, political shifts, economic signals, and cyber incidents. - Extracted events include geographic coordinates, severity, and temporal analysis such as `ongoing`, `past`, `forecast`, or `analysis`. - Named-entity recognition captures people, organizations, countries, and locations referenced in each event. #### Situation Room integration - Y2-sourced events appear on the interactive globe alongside external OSINT feeds. - Y2 markers distinguish report-derived intelligence from direct provider observations. - Standard map filters such as category, severity, and region apply to Y2 events. #### Provenance metadata - Each Y2 event carries provenance metadata that connects the observation to report-derived extraction context. - Confidence scores from 0 to 1 reflect the extraction model's certainty about each event. - Temporal classification indicates whether an event is ongoing, historical, forecast, or analytical. ### Query report-extracted events `GET /api/v1/osint/y2-events` returns only observations with `sourceType: "y2_report"`. It accepts `category`, `severity`, `countryCode`, `limit`, and `cursor`; Bearer-token requests require the `osint:read` scope. ```bash curl -H "Authorization: Bearer y2_your_key" \ "https://api.y2.dev/api/v1/osint/y2-events?category=conflict&severity=high&limit=10" ``` The current JSON response uses stable public IDs and cursor pagination: ```json { "data": [ { "id": "obs_7Jf2kQ9mV4sP", "type": "observation", "sourceType": "y2_report", "title": "Escalation in Eastern Mediterranean Naval Activity", "description": "Multiple carrier groups repositioned...", "category": "conflict", "severity": "high", "coordinates": { "lat": 34.5, "lon": 33.0 }, "geometry": { "type": "Point", "coordinates": [33.0, 34.5] }, "locationName": "Eastern Mediterranean", "countryCode": "CY", "region": "middle_east", "eventTime": 1784023200000, "eventTimeISO": "2026-07-14T10:00:00.000Z", "occurredAt": "2026-07-14T10:00:00.000Z", "observedAt": null, "url": "https://example.com/source", "provenance": { "source": "y2_report", "confidence": 0.92 } } ], "meta": { "pageCount": 1, "limit": 10, "hasMore": false, "nextCursor": null }, "links": { "self": "/api/v1/osint/y2-events?category=conflict&severity=high&limit=10", "next": null } } ``` See the generated [`GET /osint/y2-events` reference](/docs/api/reference/osint/listY2Events) for all parameters, representations, and error responses. ### How the pipeline works ```mermaid flowchart LR report["Generate report"] --> extract["Extract events"] extract --> normalize["Normalize places and entities"] normalize --> observe["Upsert observations"] observe --> resolve["Link provenance and incidents"] ``` The pipeline identifies structured events, resolves locations and entities, and upserts each event as an observation with `sourceType: "y2_report"`. Internal lineage retains the report, model, and confidence context used during extraction. Y2-sourced observations have a 14-day time-to-live. Expired rows are excluded from API and app queries and removed by OSINT maintenance. --- ## Profile sharing defaults Community profile sharing is now **enabled by default** when creating new profiles. You can still turn sharing off in profile settings. --- ## Situation Room map enhancements - Y2 report-sourced events now render with distinct Y2-branded markers on the globe. - Map-layer rendering for Y2 provenance events was improved. --- # June 2026 Release Archive (/docs/changelog/2026-06) > Historical release notes: plan prices and quotas below describe the release at that time. > For current Elite allowances, see [Plans and Limits](/docs/plans). [← Back to the latest release](/docs/changelog) This archive preserves what changed in June 2026. Follow its links to feature and API guides for current access, limits, and request contracts. ## June 30, 2026: News Terminal, Lite trial signup, and platform reliability Y2 now includes the rebuilt News Terminal, a simpler Lite trial signup path, and reliability improvements across billing, chat, delivery, developer tools, and intelligence data quality. ### What changed #### News Terminal - The News Terminal now uses direct-source feeds and appears throughout the product as the Y2 News Terminal / Signal Feed, replacing legacy Gloria-branded copy in active customer surfaces. - Users can select source feeds, switch between Live Feed and 12h Recap views, refresh the active view, and keep feed/view preferences across reloads. - Recaps now summarize the same signals shown in the live feed, keeping summaries aligned with the terminal while reducing unnecessary source refreshes. - News API and reference docs now describe the Y2 News Terminal consistently, with clearer responses for invalid topics, invalid timeframes, and feeds that are still initializing. #### Lite trial signup and billing - New signup now guides users toward a Lite trial by default while keeping Free available as a fallback path. - Lite checkout starts a 7-day trial and returns users to briefing setup once subscription access is confirmed. - If checkout finishes before subscription access has synced, users now see a pending setup state with actions to check again or return to plan selection instead of being redirected prematurely. - Trialing subscriptions now unlock paid access consistently across briefing setup, chat, usage tracking, and billing views. - Billing views now show clearer workspace subscription status, owner-only billing controls, and profile reclaim notices after plan changes. #### App, Chat, and delivery reliability - App shell navigation and scrolling are more stable on mobile and desktop. - Chat, profile setup, report delivery, audio, SMS, and webhook workflows received reliability improvements around usage tracking, delivery state, and customer-facing errors. - Report and briefing layouts now handle long reports, delivery preferences, branding, and profile setup more clearly. #### Developer and intelligence workflows - API Workbench endpoints are now grouped into collapsible categories, making larger API surfaces easier to scan. - Generated API examples keep pasted API keys out of copied snippets, and the workbench includes a copyable AI Quickstart prompt for teams integrating Y2. - Aircraft tracks and normalized entity identifiers now feed the intelligence graph more consistently, reducing duplicate entities and improving relationship context across OSINT and Explorer views. --- ## June 18, 2026: Chat reliability, OSINT Monitor, and landing performance Y2 now hardens chat image generation, streamlines the OSINT monitor workspace, and adds landing-page performance guardrails for responsive marketing assets. ### What changed #### Chat and configuration reliability - Image generation now reserves chat credits before external model calls, refunds reserved credits on provider, upload, or storage failures, and avoids double charging when generated images are saved. - Chat stream error handling now returns a stable `INTERNAL_ERROR` code and logs redacted identity metadata instead of exposing raw token or subject values. - Convex client setup and chat HTTP URL derivation now fail fast when `VITE_CONVEX_URL` is missing. - Open Graph report routes derive public URLs from `SITE_URL`, with `https://y2.dev` as the production fallback. - Chat web-search source chips now ignore malformed or unsupported URLs instead of throwing during render. #### OSINT Monitor experience - The monitor bottom bar now uses four resizable panes: live video, theater headlines, FININT, and intel findings. - CNBC is promoted to the first live-news channel, and the removed duplicate news terminal gives more room to the analysis panels. - Lite workspaces now unlock the full Situation Room experience: live tracking, naval vessels, GPS interference, markets, prediction signals, anomaly/convergence/cascade analysis, exports, and the full paid OSINT limits. - The monitor layer sidebar now treats every Situation Room layer as Lite-accessible, so Lite users can enable the same map layers available to Pro and Elite workspaces. #### Landing-page performance - Walkthrough images now use versioned responsive `srcSet` metadata, intrinsic dimensions, eager first-image loading, and lower-priority lazy loading for later images. - New 960px marketing image derivatives reduce walkthrough screenshot payloads, with immutable Netlify caching for `/images/marketing/*`. - `bun run check:landing-perf` validates stale hero preloads were removed, marketing images stay versioned and responsive, and responsive derivatives remain smaller than their originals. --- ## June 2026: Y2 Elite B2B, Organization Workspaces and Intel API v2 Elite pricing changed to **$200/month** or **$2,000/year** on August 31, 2026, for an organization workspace with up to **5 members**. See [Plans and Limits](/docs/plans) for current pricing and entitlements. The June launch prices below are historical. Y2 Elite launched in June as the flat-priced B2B organization plan at **$500/mo flat** or **$5,000/yr**, including one organization workspace with up to **5 members**, shared governance, audit logs, the highest API/profile/workspace limits, and expanded Intel API access. Y2 Workspaces now support the current organization model for companies, teams, and departments. Personal Free, Lite, and Pro workspaces continue to serve individual workflows; Elite creates a shared organization workspace where profiles, reports, API keys, usage, delivery settings, and governance resolve from the active workspace. ```mermaid flowchart LR people["Owner and members"] --> workspace["Active organization workspace"] workspace --> intel["Profiles and reports"] workspace --> delivery["Delivery and branding"] workspace --> api["API keys and usage"] workspace --> governance["Roles and audit logs"] ``` This release also expands the Intel API v2 surface served from `https://api.y2.dev/api/v2`. The new Intel surface is gated by `intel:finint`, `intel:cyber`, and `intel:explorer` scopes. As of July 21, 2026, new API-key access to those scopes requires Pro or Elite. Existing Lite API users retain their keys at historical Lite limits; Lite retains in-app FININT/Cyber/Explorer, and the in-app Cyber dashboard remains available on Free. ### What changed #### Organization workspaces - Elite creates a shared B2B workspace for organizations, teams, and departments. - Owners and admins can invite members, manage roles, and review workspace governance. - Workspace resources are loaded and protected server-side, including profiles, subscriptions, reports, audio, branding templates, delivery settings, API keys, and usage. - API keys belong to the active workspace and enforce workspace-scoped resource access, scopes, IP allowlists, and aggregate rate limits. #### Elite pricing and limits | Feature | Elite | |---------|-------| | June launch price (superseded) | $500/mo flat or $5,000/yr; [current pricing](/docs/plans) | | Members | Up to 5 per organization workspace | | Profiles | 50 total custom and community profiles | | Branding Templates | 25 | | API Keys | 25 | | API Rate Limits | 120 req/min, 50,000 req/day | | Chat Budget | 5,000 credits/month | | Situation Room | 50 layers, 100 alert rules, 50 watchlist countries | | OSINT Refresh & Retention | 1 min refresh, 90 day snapshot retention | | Pronunciation Dictionaries | 25 | Elite includes 10x Pro daily API capacity, 5x Pro API-key capacity, 5x Pro chat budget, 2.5x Pro profile capacity, and organization-only governance. #### B2B governance and safety - Workspace members and audit logs are available on Elite organization workspaces. - Team usage summaries and API-key activity are available from workspace governance views. - Invite acceptance activates the paid organization workspace while preserving each user's separate personal Free workspace. - Free-trial report limits remain user-global, so creating additional workspaces does not mint new trial capacity. #### Intel API v2 The new Intel API exposes Y2's ontology layer through HTTP endpoints: deduplicated incidents, canonical entities, entity-relationship graphs, prediction and Y2 markets, emergent signals, and financial intelligence. It serves the same data as the in-app Explorer, FinInt, and Cyber sections. The 11 endpoints shipped in this launch accept both API keys and [x402 pay-per-request](/docs/api/x402): The additive `GET /api/v2/changes` feed was released later. The current surface therefore has 12 paths; use the [Intel API guide](/docs/api/intel) and generated reference for the live inventory. | Method | Endpoint | Tier | Scope | |--------|----------|------|-------| | `GET` | `/api/v2/incidents` | T1 ($0.001) | `intel:explorer` (or `finint`/`cyber` by category) | | `GET` | `/api/v2/incidents/{id}` | T2 ($0.002) | scope resolved from incident category | | `GET` | `/api/v2/entities` | T1 ($0.001) | `intel:explorer` | | `GET` | `/api/v2/entities/{id}` | T2 ($0.002) | `intel:explorer` | | `GET` | `/api/v2/entities/{id}/graph` | T3 ($0.01) | `intel:explorer` | | `GET` | `/api/v2/markets` | T1 ($0.001) | `intel:finint` | | `GET` | `/api/v2/finint` | T1 ($0.001) | `intel:finint` | | `GET` | `/api/v2/signals` | T3 ($0.01) | `intel:finint`, `intel:cyber`, or `intel:explorer` by domain | | `GET` | `/api/v2/cyber/graph` | T3 ($0.01) | `intel:cyber` | | `GET` | `/api/v2/cyber/cves` | T1 ($0.001) | `intel:cyber` | | `GET` | `/api/v2/cyber/actors` | T1 ($0.001) | `intel:cyber` | New Intel scopes: - `intel:finint` — markets, finint indicators, non-cyber incidents, market/supply-chain signals - `intel:cyber` — CVEs, threat actors, malware families, cyber graph, cyber incidents, cyber/technology signals - `intel:explorer` — universal entity catalog, entity drill, entity graph, any-category incidents, broad signal queries Every v2 response stamps `X-Y2-API-Version: 2.0` so programmatic clients can branch on surface. The in-app [API Workbench](/app/developers/api-workbench) lists Intel endpoints in a dedicated Intel group, the full reference lives in [Intel API documentation](/docs/api/intel), and the OpenAPI spec at `/api/openapi.yaml` includes all 11 v2 paths with operation-level `servers:` overrides. #### Everything in Pro, plus team scale Elite includes Pro capabilities such as full Situation Room access, audio narration, SMS and webhook delivery, scheduled reports, advanced research, FININT/Cyber/Explorer API scopes, investigation sharing, live/vessel/market tracking, anomaly/convergence/cascade analysis, and data export. ```bash curl -H "Authorization: Bearer $Y2_API_KEY" \ "https://api.y2.dev/api/v2/incidents?category=cyber&severity=high&limit=10" ``` --- # July 2026 Release Archive (/docs/changelog/2026-07) > Historical release notes: plan prices and quotas below describe the release at that time. > For current Elite allowances, see [Plans and Limits](/docs/plans). [← Back to the latest release](/docs/changelog) This archive preserves what changed in July 2026. Follow its links to the maintained feature and API guides for current behavior. ## July 30, 2026: Privacy, product platform, and installed-app foundation Y2 strengthens its privacy terms, simplifies product analytics and feedback, and lays the foundation for installed desktop and Android applications. ### Clearer data-processing terms - The [Data Processing Agreement](/dpa) now defines customer and Y2 processing roles, United States service-provider restrictions, security and breach duties, deletion and audit support, and international transfer safeguards. - New processing, subprocessor, and security schedules explain the data involved, each provider's purpose, and Y2's technical and organizational safeguards. - Customers now receive at least 30 days' notice before a new subprocessor materially processes Customer Personal Data. ### One consent-controlled product platform - Y2 removed Statsig and UserJot and consolidated product analytics, masked session replay, surveys, and in-app Support in PostHog. - Optional browser analytics remain consent-controlled. Session replay masks visible text and form inputs, and reviewed product events exclude user-authored report, profile, and investigation content. - The Privacy Policy, DPA subprocessor schedule, and internal data-privacy audit now describe the same current processing. ### Platform and installed-app foundation - The web application and backend now use a shared Bun and Turborepo workspace, with common translation and desktop contracts. - Electron and Android shells add secure renderer boundaries, scoped device permissions, update and packaging checks, and shared application behavior. Native signing and store distribution remain separate release gates. --- ## July 24, 2026: Projects, Automations, and workspace governance Y2 added private Project workspaces and durable Agent Y2 Automations with plan-aware access, workspace isolation, and Elite governance. ### Projects on Lite, Pro, and Elite - **Projects** organize chats, reports, profiles, graph boards, generated images, evidence, and private documents around one objective. - Graph, Timeline, Situation, Evidence, Knowledge, and Overview views keep related intelligence connected without publishing Project content to Global Knowledge. - Projects are bound to the active workspace and their owner. Switching workspaces cannot expose another workspace's Project chats, resources, evidence, or private knowledge. - Project sharing was not included in this release; Elite members did not automatically gain access to another member's private Projects. Follow the [Projects overview](/docs/projects) or [create your first Project](/docs/projects/getting-started). ### Automations on Pro and Elite - **Automations** run bounded Agent Y2 instructions daily, weekly, monthly, or after a selected InfoOps profile completes a report. - Pro includes up to **10** Automation definitions and Elite includes up to **50**, shared across the active workspace. Free and Lite do not include Automations. - An Automation can write to a workspace-bound output thread or stay inside an active Project. - Live admission checks stop stale, overlapping, cross-workspace, downgraded, or inaccessible Project runs before paid work continues. - Run history records trigger, definition revision, output, status, and billing settlement; owners can safely pause or archive definitions after a downgrade. See [Automations](/docs/automations) and the [Automation reference](/docs/automations/reference). ### Workspace governance and language support - Elite audit records bind material Project and Automation changes to the acting user and workspace without changing owner-private content access. - Project and Automation navigation, plan guidance, upgrade states, forms, run history, and landing explanations are available in English and Spanish. - Plan, workspace, report-workflow, and profile documentation reflects the same enforced entitlement boundaries. --- ## July 21, 2026: Global coverage, Chat updates, and Pro API keys Y2 expanded global intelligence coverage, simplified active research, and tightened API-key access. ### Intelligence - New Situation Room visitors start with all **27 map layers** enabled. Existing preferences stay unchanged, and mobile markers are easier to select. - Discover adds monthly digests for **72 cities** and **39 industries**. Chat and onboarding now recommend a matching public digest before creating a duplicate custom brief. - News Terminal expands to **40 global topics** across geopolitics, markets, industries, regions, AI, and crypto. High-signal items join Y2's ontology with deduplication, entity links, and source provenance. - Incident enrichment and corroboration now run together, with automatic recovery for interrupted work and protection from stale retries. Explore [News Terminal](/docs/news), [OSINT sources](/docs/osint/sources), or the [News API](/docs/api/news). ### Chat - New chats default to **GLM 5.2** using Zero Data Retention routing. Existing threads and saved model choices stay unchanged. - Featured models are now Nemotron 3 Ultra, Grok 4.5, GLM 5.2, DeepSeek V4 Pro, Kimi K3, Qwen 3.6, GPT-5.6 Luna, Claude Sonnet 5, Gemini 3.6 Flash, and Mistral Small. - Tool activity now uses a compact, auto-scrolling panel with clearer progress and failure states. See [Chat Features](/docs/chat) for model selection and privacy details. ### API access - New API keys require **Pro** (5 keys, 30 requests/minute, 5,000/day) or **Elite** (25 keys, 120 requests/minute, 50,000/day). - Existing Lite keys keep their historical limits (10 requests/minute, 500/day) and can still be rotated or revoked. Lite cannot create more keys, and future downgrades are not grandfathered. Webhooks remain available on Lite, and x402 access is unchanged. See [API Authentication](/docs/api/authentication) or [x402 pay-per-request access](/docs/api/x402). --- ## July 17, 2026: Y2 Affiliate program Every Y2 user can review the Affiliate Program in **Settings → Referrals**. A personal FirstPromoter referral link is created and displayed only after the user explicitly accepts the current program terms. The enrolled dashboard keeps provider credentials and raw customer details server-side. - The default Y2 Affiliate Program offers 15% recurring commission on eligible paid Lite and Pro subscription revenue. - Referral attribution converts only after a new customer completes a verified non-zero Lite or Pro payment; a signup, 7-day trial, Free or Elite purchase, or fully discounted invoice does not create a commissionable referral. - A promoter must maintain an active paid Lite, Pro, or Elite workspace and keep their own Y2 payments current throughout commission eligibility, review, and payout. - Pending commissions remain subject to the 60-day payment, refund, fraud, and eligibility review. See the [Affiliate Program guide](/docs/affiliate-program) for enrollment, attribution, payout, and troubleshooting details. --- ## July 10, 2026: Privacy-first Chat and connected intelligence Y2 makes model selection clearer, keeps provider routing privacy-first, and connects Situation Room signals to the wider intelligence workspace. ### What changed #### Privacy-first Chat - Y2 Chat and Agent Y2 route model requests through Zero Data Retention (ZDR) endpoints, with provider data collection disabled. See [Chat Features](/docs/chat) and the [Agent Y2 API guide](/docs/api/agent-y2). - The model picker now shows readable names, provider filters, and search. - GPT-5.6 Luna and NVIDIA Nemotron 3 Ultra are featured chat options, subject to current provider availability. #### Situation Room and connected intelligence - The map now groups orbital and satellite layers, surveillance sources, live media, and expanded infrastructure and military overlays in shared layer controls and a source catalog. - The monitor tool rail places Intel Findings, Network Intel, ontology Search, and API Query beside the map and loads each tool on demand. - Live malware monitoring now includes relationship graphs for indicators, malware families, and related entities. - Search the Y2 ontology directly from the Situation Room to explore entities and their live relationships. - Map marker graphs now show richer source, confidence, time-range, and report context, with a direct link to the related historical report when available. - Monitor controls and data panels now adapt to smaller screens and simplify navigation. #### Reliable scheduled reports - Scheduled report generation now detects interrupted runs, queues bounded recovery attempts, and keeps the profile active instead of delivering incomplete reports. - Profile status and retry state are clearer when a report needs another attempt. See [Scheduling](/docs/profiles/scheduling). #### Clearer plan guidance - Plan and branding documentation now matches current access: the full Situation Room is available on Lite, Pro, and Elite, while reusable report branding is available on Pro and Elite. See [Whitelabel Branding](/docs/profiles/branding). --- ## July 8, 2026: Agent Y2 API, OpenAI-compatible Chat, and MCP Y2 now exposes Agent Y2 through API-key authenticated streaming, an OpenAI-compatible chat-completions route, a standalone MCP server for agent clients, and refreshed OpenAPI, client-integration, and endpoint documentation. ```mermaid flowchart LR client["Agent client"] --> native["Agent Y2 stream"] client --> compatible["OpenAI-compatible stream"] native --> executor["Shared Agent Y2 executor"] compatible --> executor executor --> tools["Scoped Y2 tools"] ``` ### What changed #### Agent Y2 API and OpenAI-compatible Chat - Agent Y2 is now available through API-key authenticated streaming at `POST /api/v1/agent-y2/chat/stream`; see the [Agent Y2 API guide](/docs/api/agent-y2) and [generated Agent Y2 reference](/docs/api/reference/agent-y2). - The OpenAI-compatible `POST /api/v1/chat/completions` route supports `model: "y2-agent"` for streaming clients that already consume OpenAI-style chat completion chunks. - Both routes require the `agent:y2` API-key scope, use workspace-aware entitlements and chat credit budgets, and attribute usage back to the correct workspace, thread, endpoint, and API key. - Agent Y2 API access is intentionally API-key only in this release, with a shared `5/min`, `100/day` per-key throttle and a `10/min`, `250/day` workspace/user throttle across both Agent Y2 API routes. - OpenAI-compatible stateless chat calls preserve caller-provided transcript context when no Y2 thread is attached, while threaded calls validate ownership before reserving chat credits. #### MCP, OpenAPI, and client documentation `@y2-intel/mcp` is distributed separately from this platform repository. The maintained [MCP guide](/docs/api/mcp) explains that boundary and shows how to inspect the capabilities of the installed package version. - New [Y2 MCP docs](/docs/api/mcp) cover the standalone `@y2-intel/mcp` stdio server for Claude, Codex, Claude Desktop, and other MCP-compatible clients. - The MCP server exposes Y2 docs and OpenAPI resources, bounded report and news tools, an operation lookup tool, and `y2_ask_agent` for Agent Y2 access. - The [OpenAPI specification](/docs/api/openapi), [Endpoint Reference](/docs/api/reference), and [client guides](/docs/sdks) now reflect the canonical Agent Y2 routes, `agent:y2` scope, request schemas, response codes, generated examples, and rate-limit headers. - The [API Workbench](/docs/api/workbench) and docs navigation received sidebar, category, route, theme-state, and 404 handling improvements for the expanded API surface. #### Agent reliability and synthesis safety - The shared Agent Y2 executor now powers both app-authenticated and API-key routes, keeping the fixed Agent Y2 system prompt protected from caller override. - Agent Y2 API responses hide internal reasoning chunks and align SDK examples to the canonical `POST /api/v1/chat/completions` path. - Degraded Y2 synthesis now fails closed instead of completing reports from low-confidence output, with debugging documentation added for report-generation degradation triage. --- # August 2026 Release Archive (/docs/changelog/2026-08) [← Back to the latest release](/docs/changelog) This archive preserves what changed in August 2026. Follow its links to the maintained feature and API guides for current behavior. ## August 25, 2026: Native WebMCP support Y2 now exposes native WebMCP tools inside the authenticated web app so compatible browser agents can understand the current view and help users move through the product. ### Context-aware browser tools - Agents can inspect the current authenticated Y2 view and navigate to advertised product areas. - Project pages expose compact tools for listing and opening projects in the active workspace. - InfoOps pages expose compact tools for listing and opening personal or subscribed briefings. ### Scoped by design - WebMCP is registered only inside the authenticated app and uses existing workspace and profile access controls. - The initial tools cannot change account or intelligence data; open actions only navigate the current tab. - Unsupported browsers continue to use Y2 without any behavior change. --- ## August 22, 2026: MCP expands agent access Y2 MCP 0.2.0 gives agents broader read access, live API discovery, and explicit controls for actions that can change account data. ### Broader agent access - The local MCP server now exposes 52 tools by default across reports, News Terminal, Situation Room, Intel v2, profiles, Projects, Automations, webhooks, and public receipt lookup. - Agents can inspect Y2 documentation and list API operations without an API key, then use scoped keys for protected data. - `y2_list_api_operations` and `y2_call_api` let agents discover and call endpoints from the live OpenAPI contract when a dedicated typed tool is not available. ### Safer optional actions - Profile, Project, Automation, webhook, and subscription-delivery mutations remain hidden unless write tools are explicitly enabled and the API key has the matching scope. - Agent Y2 remains disabled by default and requires a separate opt-in plus the `agent:y2` scope. - Transient rate-limit and service failures are retried automatically before reaching the client. See the [MCP setup guide](/docs/api/mcp), [authentication and scopes](/docs/api/authentication), and [OpenAPI guide](/docs/api/openapi). --- ## August 7, 2026: Reliable API results, safer access, and updated plan limits Y2 makes API results more complete and predictable, strengthens access checks around Automations, and updates profile limits across Free, Lite, and Pro. ### More reliable API results - Filtered list endpoints now continue through their underlying data until they can return a full matching page or reach the end, preventing false-empty and incomplete pages. - API errors use stable public codes and messages without exposing internal Convex details. - Response validation and the API contract now agree across reports, projects, Automations, profiles, webhooks, and Situation Room data. - API Workbench responses preserve documented JSON, text, and audio behavior more consistently. See the [API overview](/docs/api), [OpenAPI guide](/docs/api/openapi), and [API Workbench](/docs/api/workbench). ### Safer Automations - Automation runs revalidate workspace write access before reserving or spending credits. If access changes during a run, Y2 stops the work and settles the reservation safely. - API authorization failures now return the same status and error shape across Project and Automation routes. ### Updated profile limits and Lite trial - Pro supports up to **5 custom profiles**, up to **20 community or global subscriptions**, and a **20-profile combined workspace total**. - Lite continues to include **1 custom profile** and **3 community subscriptions**, does not include reusable branding templates, and starts with a **7-day trial** during onboarding. - Free supports **1 global or community profile total**. Elite limits and pricing are unchanged. See [Plans and Pricing](/docs/plans) and [Creating Profiles](/docs/profiles/creating). --- ## August 5, 2026: Connected Explorer, Documents, and Project Evidence Y2 makes private documents easier to organize, automatically connects report sources to Project Evidence, and opens entity exploration with useful relationship context. ### Connected Explorer neighborhoods - Opening an entity in Ad-hoc Explorer now loads a focused neighborhood of related entities and the connections between them instead of leaving an isolated node. - Selecting another entity expands around it while preserving the temporary graph, making it easier to compare related intelligence before adding it to a Project. - Y2 Copilot now works from the active temporary graph and selected entity, keeping its analysis grounded in what is visible without treating the session as a saved Project. - Clear loading and retry states distinguish a relationship lookup from an entity with no known connections. ### Broader private document support - Documents now supports PDFs, word-processing files, spreadsheets, presentations, ebooks, and common text formats for private knowledge. - PDFs retain page structure for more precise retrieval, while files that cannot be read reliably return a clear processing status instead of incomplete content. - A simplified Documents library shows where each file is available, its current status, and the actions needed to manage or restore it. ### Report sources become Project Evidence - Linking a profile to a Project now adds supported web sources from its existing reports to Evidence and continues adding sources from future reports. - Evidence keeps its connection to the originating report and source, making it easier to review the material behind an investigation. - Sync status and retry controls make it clear when sources are still being added or need another attempt. See the [Projects overview](/docs/projects) and [organizing intelligence in Projects](/docs/projects/organizing-intelligence). --- # September 2026 Release Notes (/docs/changelog/2026-09) [← Back to the latest release](/docs/changelog) ## Upcoming ### More resilient report completion - If one report-writing path cannot accept the required report format, Y2 continues through supported alternatives, giving the report another chance to finish from the same collected evidence. - This applies to reports generated after the update. Reports that already ended in failure remain unchanged and may still need to be retried. ### More reliable report and signup delivery - A report no longer repeats its full generation cycle when a saved delivery channel has no reachable contact details. Other recipients can continue receiving the report. - Signup reminder emails can be prepared and queued without interrupting the scheduled reminder. - An unreachable recipient still needs updated delivery details before they can receive a report. Earlier failed reports and reminders are not sent again automatically. ### More dependable connected intelligence and Project Evidence - Placeholder identifiers no longer interrupt connected-intelligence updates for unrelated products or assets. - Project Evidence can show lineage details for linked reports and other supported records. - Existing identity conflicts still require review and are not combined automatically. ## September 17, 2026 private chats, conversation controls, and reliability Start a temporary conversation or remove a saved one, with controls that fit the existing desktop and mobile Chat layout. Updates also improve workspace switching, report retries, and account deletion feedback. ### Private chats - Choose **Private** at the top right of Chat to start a private conversation. Choose it again to return to regular Chat. - Private conversations stay out of your history and clear when you leave or reload the page. - Private chats use workspace Agent Y2 credits at the same rates as regular chats, including billable tool use. A reminder stays visible below the composer. - Uploads, memory, and saved actions remain available in regular chats. ### Easier conversation cleanup - Open a conversation's history menu or use the delete control in its header to delete it. - Confirm before permanently removing the conversation and its messages. Credits already used are not refunded. - Long conversation titles leave room for the history menu, keeping its actions visible. ### Aligned mobile controls - Private mode keeps its toggle at the top right, with a centered explanation and the composer above the existing bottom navigation. - Larger touch targets and safe-area spacing keep the new private-chat and deletion controls easy to reach. ### Smoother updates and workspace changes - Returning to an older open tab shows a reload notice when a newer version is available, giving you time to save a draft before reloading. - Switching accounts, workspaces, or plans clears requests from previous views. Workspace pages wait for access to be ready before loading their content. - Members can view their workspace's member list without requesting restricted invitation data. - A failed command-menu preload no longer produces an unhandled error. ### More dependable reports and OSINT updates - Retrying the same report-generation run reuses a saved report instead of creating a duplicate. - Reports with missing workspace ownership stop before paid research begins, so the configuration can be corrected before trying again. - Conflicting entity identities can be reviewed while other OSINT records continue updating. - CVE collection retries temporary service failures from the same page and time window. ### Clearer account deletion - Account deletion shows actionable errors and confirms when a request has been accepted. - A sign-out failure after an accepted request no longer appears as a failed deletion. ## September 14, 2026 Project intelligence PDF exports Create a PDF from the materials saved to a Project, bringing findings, evidence, and visual context together for review. ### One-page briefs and full reports - Choose **Export PDF** from a Project to prepare a report. - The default **one-page intelligence brief** leads with a synthesized bottom line, up to three cited findings, implications, and what to watch next. - **Full intelligence report** adds deeper analysis, uncertainties, selected supporting passages, and visual context. Both formats summarize large Projects before rendering. - Selection prioritizes recent, relevant evidence, including up to two recent reports per linked profile. Older evidence is labelled as context; saved dates are separate from the preparation date. Exports do not perform a live source refresh. - Coverage notes identify sampled history, selected document passages, and unavailable content. Original files, complete reports, and full conversation history remain in Y2. - Selected conversation excerpts are optional in full reports and excluded by default. - Synthesis uses workspace AI credits. If synthesis is unavailable, the PDF clearly identifies its content as saved evidence excerpts instead of presenting a new assessment. - Fixes a browser security policy error that could prevent either PDF format from generating. - Brief and full exports share the same assessment while the dialog remains open and branding and conversation settings stay unchanged. Full reports expand it with supporting analysis and an evidence matrix. Reopen the dialog to prepare a new edition. - Assessment generation distinguishes direct Project evidence from broader context, preserves source families, and checks for repeated findings and elapsed milestones described as future events. ### Readable graphs and location maps - Cited graphs combine known entity identities and select a connected explanation, with readable node labels, types, directed arrows, and relationship keys. Unresolved aliases remain separate. - Maps consolidate repeated event references under stable place keys. Distinct events at one location remain separate in the key; repeated representations do not become extra locations. - Regional and unknown geometry is labelled as context. Recorded points retain coordinates; qualifying groups of nearby points can receive detail panels alongside the overview. - Visual selection states material omissions. Missing facilities, coordinates, and finer map detail are not invented; complete source graphs and records remain in Y2. ### Y2 or workspace branding - Y2 branding is the default. Workspaces with branding enabled for their plan can select an existing branding template, including its name, colors, logo, and disclaimer. - PDFs include Project identity and source references so readers can return to the supporting material in Y2. - Visual branding does not replace the report's intelligence purpose with a template persona. ## September 12, 2026 update Explore more of the observations available to Y2, give your team more room to work with Elite, and move more easily between Chat and your investigations. Country cards also gain more dependable news coverage and clearer geographic attribution. ### More complete OSINT map results - The OSINT Monitor loads all stored observations matching your selected area, time range, and event layers, removing the previous overall result cap. - Loading progress shows how many observations have arrived and when the selection is complete. If loading stops, retry from where it left off. If a refresh fails, the previous completed map remains visible. - Both globe and flat views group nearby observations into markers with counts. Clicking a group opens its event list immediately; select an observation to view its location and details, including when several observations share the same location. - Switching to flat view downloads a smaller map renderer. - Zooming and panning within a loaded region reuses its observations. Moving beyond that region loads new coverage after you finish moving the map. - Theater Intel keeps its regional headlines while you explore the map, with independent refreshes that do not depend on zoom, panning, or visible map layers. - Completed selections refresh automatically after five minutes, with a manual refresh option when you want to check sooner. ### More dependable satellite fire observations - Fire collection includes NASA FIRMS detections from Suomi NPP, NOAA-20, NOAA-21, and MODIS, and removes the previous per-region observation cap. - Observation times reflect the satellite acquisition time in UTC. Repeated collections of an unchanged detection no longer make it appear newly updated. - Clear warnings identify delayed or incomplete fire collection, even when all available map observations have finished loading. Map completion means all matching observations stored by Y2 have loaded. Fire collection still covers the existing nine monitored regions, excludes low-confidence detections, and requests today's and yesterday's observations. This update does not add global fire coverage or a 30-day fire history. See [OSINT sources and coverage](/docs/osint/sources). ### More dependable country news - Country cards select eligible news before filling their eight-headline list, so a surge in satellite fire detections no longer hides available stories. - Geopolitical and maritime RSS coverage better recognizes attacks, seizures, captures, and shipping disruptions, including Red Sea escalation headlines. - Country attribution gives priority to headline locations and specific places over countries mentioned as sponsors or background context. Regional exposure remains distinct from where an event occurred. - Reused publisher identifiers no longer replace a different stored article, preserving its identity and evidence links. - Failed news requests show a retry option instead of an empty-news message. The country-news API keeps its response fields and public observation IDs. After this update, integrations with an older country-news pagination cursor must restart from the first page if they receive an invalid or obsolete cursor response. See [Country Intelligence Cards](/docs/osint/country-cards) and [country-news API guidance](/docs/api/osint#read-country-news). ### More capacity for Elite teams Elite's shared workspace allowances expand to **10× Pro** across research, publishing, Automations, and API access. | Allowance | Elite | | --- | ---: | | Agent Y2 credits per month | 10,000 | | Custom profiles | 50 | | Total profiles, including subscriptions | 200 | | Automations | 100 | | API keys | 100 | | Default API requests per minute | 300 | | Default API requests per day | 50,000 | | Branding templates | 100 | | Pronunciation dictionaries | 100 | These allowances are shared across the organization workspace. Elite includes **five members**, with larger organizations available by arrangement, at **$200/month or $2,000/year**. Situation Room coverage, refresh frequency, and retention remain the same as Pro. See [Plans and Limits](/docs/plans) and [Workspaces](/docs/workspaces). ### Easier sign-in and mobile navigation - The blog listing loads less data, and links between documentation and articles navigate more smoothly. - Opening the Y2 home page while signed in takes you directly to Chat. - Mobile tabs stay easier to reach across intelligence, developer, and settings views, with fewer unexpected page jumps when switching tabs. - Explorer gives mobile users clearer access to search and investigation controls. ### New guides for intelligence and AI workflows New practical guides cover [OSINT with MCP](/blog/mcp-server-for-osint), [scoped API keys for agents](/blog/mcp-scoped-api-key), and [keeping useful context across agent sessions](/blog/your-ai-agents-memory-resets-every-session-thats-a-workflow-problem). Learn how Y2 connects cyber, market, and global risk in the new [guide to Y2's ontology](/blog/y2-ontology-cyber-market-global-tracking). --- # Platform Changelog (/docs/changelog) Follow product changes across intelligence coverage, Chat, API access, and platform operations. ## Upcoming ### More resilient report completion - If one report-writing path cannot accept the required report format, Y2 continues through supported alternatives, giving the report another chance to finish from the same collected evidence. - This applies to reports generated after the update. Reports that already ended in failure remain unchanged and may still need to be retried. ### More reliable report and signup delivery - A report no longer repeats its full generation cycle when a saved delivery channel has no reachable contact details. Other recipients can continue receiving the report. - Signup reminder emails can be prepared and queued without interrupting the scheduled reminder. - An unreachable recipient still needs updated delivery details before they can receive a report. Earlier failed reports and reminders are not sent again automatically. ### More dependable connected intelligence and Project Evidence - Placeholder identifiers no longer interrupt connected-intelligence updates for unrelated products or assets. - Project Evidence can show lineage details for linked reports and other supported records. - Existing identity conflicts still require review and are not combined automatically. ## Current release: September 17, 2026 Start a temporary conversation or remove a saved one, with controls that fit the existing desktop and mobile Chat layout. Updates also improve workspace switching, report retries, and account deletion feedback. ### Private chats - Choose **Private** at the top right of Chat to start a private conversation. Choose it again to return to regular Chat. - Private conversations stay out of your history and clear when you leave or reload the page. - Private chats use workspace Agent Y2 credits at the same rates as regular chats, including billable tool use. A reminder stays visible below the composer. - Uploads, memory, and saved actions remain available in regular chats. ### Easier conversation cleanup - Open a conversation's history menu or use the delete control in its header to delete it. - Confirm before permanently removing the conversation and its messages. Credits already used are not refunded. - Long conversation titles leave room for the history menu, keeping its actions visible. ### Aligned mobile controls - Private mode keeps its toggle at the top right, with a centered explanation and the composer above the existing bottom navigation. - Larger touch targets and safe-area spacing keep the new private-chat and deletion controls easy to reach. ### Smoother updates and workspace changes - Returning to an older open tab shows a reload notice when a newer version is available, giving you time to save a draft before reloading. - Switching accounts, workspaces, or plans clears requests from previous views. Workspace pages wait for access to be ready before loading their content. - Members can view their workspace's member list without requesting restricted invitation data. - A failed command-menu preload no longer produces an unhandled error. ### More dependable reports and OSINT updates - Retrying the same report-generation run reuses a saved report instead of creating a duplicate. - Reports with missing workspace ownership stop before paid research begins, so the configuration can be corrected before trying again. - Conflicting entity identities can be reviewed while other OSINT records continue updating. - CVE collection retries temporary service failures from the same page and time window. ### Clearer account deletion - Account deletion shows actionable errors and confirms when a request has been accepted. - A sign-out failure after an accepted request no longer appears as a failed deletion. [Read the September 2026 release notes →](/docs/changelog/2026-09) ## September 14, 2026 ### Project intelligence PDF exports Turn saved Project intelligence into a clean one-page brief or a full report, with supporting graphs, location context, and consistent branding. - **Lead with the bottom line:** the default one-page brief synthesizes recent evidence into cited findings, implications, and what to watch, focused on your Project objective. - **Handle large Projects:** recent report history and selected source passages are summarized before PDF generation. Full reports add analysis, supporting excerpts, and visual context. Coverage notes identify sampled history and unavailable content; originals remain in Y2. - **Read one coherent assessment:** switch between brief and full formats using the same assessment while branding and conversation settings stay unchanged. Full reports add supporting analysis and a compact evidence matrix. - **See meaningful connections and locations:** cited graphs combine known entity identities and show relationship keys. Maps consolidate repeated event references by place, distinguish regional context from recorded points, and retain stable location keys. - **Choose your branding:** use Y2 branding by default or select a saved workspace branding template when workspace branding is enabled for your plan. - **Reliable browser exports:** fixes a browser security policy error that could prevent PDF generation. [Read about Project PDF exports in the September release notes →](/docs/changelog/2026-09#september-14-2026-project-intelligence-pdf-exports) ### Y2 desktop apps This release introduces Y2 Desktop for macOS and Windows, bringing Chat, Projects, reports, and your intelligence workspace into a dedicated app. Public installer downloads remain unavailable until signing and installation checks are complete. Mac downloads are deferred pending Apple signing and notarization. [Read the desktop app guide →](/docs/apps) · [Check download availability →](/download) ## September 12, 2026 ### More complete monitoring and higher Elite limits This update brings more observations and country news into the OSINT Monitor, expands Elite workspace allowances, and improves navigation across Chat, documentation, and the blog. - **Explore more observations:** load all stored observations matching your selected map area, time range, and event layers, with clear progress, retry controls, and grouped markers that let you inspect individual observations. - **Keep regional context in view:** Theater Intel headlines stay available while you pan, zoom, or change map layers, and grouped markers open their observation lists directly. - **Find relevant country news:** satellite fire detections no longer crowd headlines out of country cards. Improved geographic attribution and escalation coverage help connect stories to the right country, with a retry option when news cannot load. - **Follow satellite fire activity:** expanded NASA FIRMS collection includes more satellite products, more accurate observation times, and clearer warnings when collection is incomplete. - **Do more with Elite:** shared workspace allowances expand to 10× Pro for Agent Y2 credits, profiles, Automations, API access, branding templates, and pronunciation dictionaries. - **Move through Y2 more easily:** signed-in home visits open Chat, and mobile navigation keeps tabs and Explorer controls easier to reach. Documentation and blog links navigate more smoothly. - **Understand connected intelligence:** a new [guide to Y2's ontology](/blog/y2-ontology-cyber-market-global-tracking) explains how cyber, market, and global-risk observations connect through shared entities and evidence. [Read the September 2026 release notes →](/docs/changelog/2026-09) ## Report and intelligence reliability These changes improve report retries, source imports, news updates, and incident details, with clearer context to help support teams investigate failed reports. ### More resilient report retries - Keeps already accepted research when a temporary report-writing problem triggers a retry, avoiding repeat source collection and giving the report another chance to finish from the same evidence. - This applies to reports started after the update; reports already underway continue with their existing recovery behavior. ### Reliable Project Evidence - Fixes a problem that can prevent report sources from being added to Project Evidence. ### More dependable Situation Room updates - Reduces interruptions when several news items update the same incident, helping related coverage stay connected. ### Reliable incident details - Restores incident details when a location includes normalized geographic identifiers, keeping affected Situation Room and API views available. ### Better support for report failures - Gives support clearer context about the affected profile and workspace when a report fails, without adding report prompts or content to these error details. ## September 1, 2026 Y2 Elite workspace owners and admins can now see how activity and credit consumption are distributed across their entire organization workspace. ### Workspace usage at a glance - Governance shows one shared Agent Y2 credit balance for the subscription-aligned credit period. - Informational Activity units remain grouped into API, research, deliveries, and AI for the selected UTC month; they are separate from the enforced Agent Y2 credit allowance. - Member breakdowns attribute both Agent Y2 credit consumption and Activity units without creating separate member allowances. ### Accountable API key activity - Every workspace API key shows its responsible member, active or revoked state, request volume for the current month, and latest use. - Request totals now come from recorded API activity, resolving keys that previously appeared to have zero calls. - Seat limits, API key capacity, the Agent Y2 credit ledger, Activity units, and workspace audit history remain together in one administrative view. ## Previous releases Earlier release notes are grouped into monthly archives. Each link opens the full release entry. ### August 2026 - [August 25: Native WebMCP support](/docs/changelog/2026-08#august-25-2026-native-webmcp-support) - [August 22: MCP expands agent access](/docs/changelog/2026-08#august-22-2026-mcp-expands-agent-access) - [August 7: Reliable API results, safer access, and updated plan limits](/docs/changelog/2026-08#august-7-2026-reliable-api-results-safer-access-and-updated-plan-limits) - [August 5: Connected Explorer, Documents, and Project Evidence](/docs/changelog/2026-08#august-5-2026-connected-explorer-documents-and-project-evidence) [View the complete August 2026 archive →](/docs/changelog/2026-08) ### July 2026 - [July 30: Privacy, product platform, and installed-app foundation](/docs/changelog/2026-07#july-30-2026-privacy-product-platform-and-installed-app-foundation) - [July 24: Projects, Automations, and workspace governance](/docs/changelog/2026-07#july-24-2026-projects-automations-and-workspace-governance) - [July 21: Global coverage, Chat updates, and Pro API keys](/docs/changelog/2026-07#july-21-2026-global-coverage-chat-updates-and-pro-api-keys) - [July 17: Y2 Affiliate Program](/docs/changelog/2026-07#july-17-2026-y2-affiliate-program) - [July 10: Privacy-first Chat and connected intelligence](/docs/changelog/2026-07#july-10-2026-privacy-first-chat-and-connected-intelligence) - [July 8: Agent Y2 API, OpenAI-compatible Chat, and MCP](/docs/changelog/2026-07#july-8-2026-agent-y2-api-openai-compatible-chat-and-mcp) [View the complete July 2026 archive →](/docs/changelog/2026-07) ### June 2026 - [June 30: News Terminal, Lite trial signup, and platform reliability](/docs/changelog/2026-06#june-30-2026-news-terminal-lite-trial-signup-and-platform-reliability) - [June 18: Chat reliability, OSINT Monitor, and landing performance](/docs/changelog/2026-06#june-18-2026-chat-reliability-osint-monitor-and-landing-performance) - [Y2 Elite B2B, Organization Workspaces and Intel API v2](/docs/changelog/2026-06#june-2026-y2-elite-b2b-organization-workspaces-and-intel-api-v2) [View the complete June 2026 archive →](/docs/changelog/2026-06) ### March 2026 - [Y2SOURCE Event Provenance Engine](/docs/changelog/2026-03#march-2026-y2source-event-provenance-engine) - [Profile Sharing Defaults](/docs/changelog/2026-03#profile-sharing-defaults) - [Situation Room Map Enhancements](/docs/changelog/2026-03#situation-room-map-enhancements) [View the complete March 2026 archive →](/docs/changelog/2026-03) --- # Intelligence Chat (/docs/chat) Y2 Intelligence Chat combines a selectable language model with platform tools for research, documentation, news, profiles, and intelligence data. Chat is available in active Lite, Pro, and Elite workspaces. ```mermaid flowchart LR A[Your message] --> B[ZDR-eligible model] B --> C{Tool needed?} C -->|No| D[Stream response] C -->|Yes| E[Entitled Y2 tool] E --> B D --> F[Store thread and usage] ``` ## Start a conversation ### Open Chat Select **Chat** in the signed-in app. Free workspaces see an upgrade wall because their chat credit limit is zero. ### Select a model Open the model selector and choose an available model. The list is populated from Y2's current ZDR-eligible catalog, so names and availability can change between visits. ### Describe the outcome Ask a direct question or use a quick action for web search, InfoOps setup, topic research, or Y2 documentation. ### Review tool activity The thread displays tool activity and structured result cards when the model searches data or proposes a profile change. Verify consequential results before confirming a write action. Press Enter to send and Shift+Enter for a new line. While a response is streaming, use the stop control to end generation. You can change the model for an existing thread or regenerate the last assistant response. ## Model routing and privacy The selector returns only model records marked as supporting Zero Data Retention. At request time, Y2 also asks OpenRouter to deny provider data collection, require compatible request parameters, and route only to ZDR endpoints. ZDR applies to external model routing. Y2 still stores chat threads, messages, tool results, workspace context, attachments when used, and usage records to operate the product. Tools such as web search or image generation also send the necessary prompt or query to their configured service. If a saved model is no longer ZDR-eligible, Y2 falls back to an eligible default when one exists; otherwise thread creation or model selection fails with a model-unavailable error. ## Available tools Tools are attached to a request according to the active workspace's entitlements and the selected model's capabilities. Searches the local Y2 documentation corpus for product, API, profile, and delivery questions. This tool is present in standard chat requests. Searches current external sources when recent information is needed. It is enabled for Lite, Pro, and Elite chat. Searches cached News signals, filters by text or sentiment, retrieves available recaps, and lists the current 40-feed catalog. Longer recap timeframe identifiers can be requested, but the current News registry schedules only 12-hour recaps. Searches shared/community profiles and can subscribe after confirmation. When the workspace can create custom profiles, Chat can also collect profile fields, create a profile, inspect an existing profile, preview changes, and update it. Normal profile and subscription limits still apply. Entitled paid workspaces can query Situation Room data. The current tool set includes OSINT search, entities, incidents, cyber intelligence, FININT, markets, investigations, and asynchronous investigation enrichment where the corresponding feature is enabled. Paid workspaces can generate images. A dedicated image tool is attached when the selected chat model does not provide native image generation; native-capable models handle the request directly. Generated images use their own request-scoped hold in the same organization credit ledger. A zero-cost internal tool can still be part of a metered chat response. Model input and output tokens consume Agent Y2 credits even when the tool itself adds no external-service charge. ## Write actions and confirmation Some tools can change Y2 data: - Follow a public or community profile. - Create a custom InfoOps profile. - Update profile settings or status. - Start investigation enrichment. The profile flows are designed to search the shared library first, preview proposed settings or changes, and wait for explicit confirmation before creating or updating. Read the structured card carefully, especially the profile, schedule, active workspace, and community-sharing setting. ## Agent Y2 credits One Agent Y2 credit represents $0.01 of normalized usage. The active workspace has one shared monthly pool: members, API keys, Automations, and tools do not receive separate allowances. Y2 meters model input and output using the selected model's pricing data or a fallback pricing tier when provider pricing is absent. Certain tools add usage, while internal cache reads and profile mutations currently add no separate tool charge. | Plan | Monthly Agent Y2 allowance | Agent Y2 access | | --- | ---: | --- | | Free | 0 credits | No | | Lite | 100 credits per month | Yes | | Pro | 1,000 credits per month | Yes | | Elite | 10,000 credits per month | Yes, with workspace governance views | The balance reports the allowance, settled credits, active request holds, available credits, exact period boundaries, and next replenishment time. Organization governance also attributes settled usage and active holds to members and request sources without creating per-member allowances. ### Billing periods Monthly subscriptions use the active Stripe subscription boundaries. Annual subscriptions still replenish the monthly allowance each month, anchored to the UTC date and time when the annual term began. Payment cadence never multiplies the allowance. If no active paid-subscription period is available, credits use UTC calendar months. Before provider work begins, Y2 creates a bounded, request-scoped hold. Settling converts only that request's actual cost into usage; failures release its hold; expired holds stop reducing available credits. At 80% committed usage (settled plus active holds) the UI warns, at 95% it shows a critical warning, and at 100% it disables new messages until replenishment. ## Organize and remove conversations Chat stores threads for the signed-in user and loads recent history in the sidebar. Threads can be associated with a Y2 project, renamed, switched to another eligible model, and deleted. Deleting a thread also deletes its stored chat messages. Treat deletion as permanent and export or copy anything you need first. ## Troubleshoot Chat Confirm the active workspace is Lite, Pro, or Elite. Entitlements follow the active workspace, not just the signed-in user's other subscriptions. The selector includes only models currently stored as ZDR-supported. Choose another available model; a featured or previously saved model can disappear when eligibility changes. Check the shared workspace credit meter. At 100% of the current credit-period allowance, new messages are blocked until the period resets. Tool availability depends on the active workspace entitlement, profile limits, and selected model capabilities. Switch workspaces or review plans when the requested feature is not enabled. Try another eligible model or a smaller request. Provider credit, rate, capability, or routing failures can occur even when Agent Y2 credits remain. ## Next steps Stream the agent from an application with a scoped API key. Review every field available in the profile editor. Inspect the cache that powers Chat's news tool. --- # Contribute to Y2 (/docs/community/contributing) Choose the contribution path that matches what you want to improve. Product and documentation feedback goes to Y2; public briefing profiles are published inside the application; code changes belong in the repository that owns the relevant SDK or tool. ## Choose a contribution path | Your goal | Use this path | What to include | | --- | --- | --- | | Publish a reusable briefing | Enable **Share with Community** on a workspace-owned profile | Clear topic, safe public instructions, useful tags, and reviewed output | | Report a product or documentation problem | Use PostHog Support in the desktop app or email [support@y2.dev](mailto:support@y2.dev) | Route or docs URL, expected result, actual result, and reproduction details | | Report unsafe or inaccurate public content | Email [support@y2.dev](mailto:support@y2.dev) | Profile name or URL, the specific concern, and supporting context | | Build an integration | Use the OpenAPI contract, API keys, and webhooks | Least-privilege scopes, signature verification, retries, and idempotency | | Change an SDK or CLI | Follow the contribution and license terms in that tool's own repository | Focused change, matching tests, documentation, and compatibility notes | ## Publish a community briefing Community sharing is the only in-product publication path documented here. It makes the original profile and its later reports discoverable to other signed-in Y2 users; it does not submit the profile to a review queue. ### Prepare a focused profile Use a clear name, topic, report structure, and compact set of tags. Generate and review at least one report before asking others to rely on it. ### Remove private context Review the custom instructions, sources, sections, and generated output. Remove credentials, customer data, internal names, and confidential research requirements. ### Enable sharing Open Advanced Configuration, enable **Share with Community**, and save the profile. ### Verify the public result Open [InfoOps → Discover](/app/infoops/community), search for the profile, and inspect its details as a reader would. See [Share a Profile](/docs/profiles/sharing) for the complete visibility and subscriber model. ## Report a product or documentation problem The desktop application loads PostHog Support when analytics consent is enabled. The widget is hidden on mobile and while Copilot is open. Email [support@y2.dev](mailto:support@y2.dev) when the widget is unavailable or the report needs a private channel. A useful report contains: - The application route or documentation URL. - The active workspace type and plan when the behavior is entitlement-related. - A short, repeatable sequence of actions. - The expected and actual result. - Relevant timestamps, request IDs, HTTP status codes, or screenshots. - Whether the issue is consistent or intermittent. For a documentation correction, quote only the smallest inaccurate statement and point to the current UI, API response, or other evidence that contradicts it. Never include API keys, webhook signing secrets, one-time authentication codes, payment details, or unredacted personal data. Redact authorization headers and sensitive payload fields before attaching logs or screenshots. ## Report a security concern privately Send a security concern to [support@y2.dev](mailto:support@y2.dev). Include the affected surface, impact, reproduction conditions, and a safe proof of concept. Do not publish exploit details, credentials, or another workspace's data in a community profile or public issue. Y2's local product repository does not define a separate public security-policy file, so this page does not promise a public issue tracker, bounty, response time, or disclosure schedule. ## Build an integration Use the documented API contract rather than depending on application internals. Generate clients from the same specification used by the endpoint reference Create tenant-scoped keys and grant only the required scopes Verify signatures and design receivers for retries and duplicate delivery Start from task-oriented API patterns There is no documented public integration marketplace or automatic submission process in the current application. Contact support before presenting an integration as endorsed by Y2. ## Work on an SDK or CLI The SDK pages link to their current source and package locations. Before changing a tool, open its own repository and verify its README, contribution instructions, tests, release process, and license. Those repository-local rules take precedence over this general guide. Review the TypeScript package, examples, and linked source repository Review the Python package, examples, and linked source repository Review CLI installation, commands, and linked source repository The Y2 platform repository identifies itself as proprietary, and the API contract uses proprietary terms. Do not assume that the platform, documentation, examples, and external SDK repositories share one license. Check the license attached to the exact artifact you plan to use or modify. ## Contribution checklist - Keep the change focused on one user problem. - Verify examples against the current API contract or application behavior. - Include tests when the target repository has a test suite. - Update the documentation affected by a behavior or interface change. - Avoid promising badges, editorial review, promotion, credits, or response times unless Y2 has confirmed them for the specific contribution. - Use [@y2_intel on X](https://x.com/y2_intel) for public updates; use support channels for actionable reports. --- # Community (/docs/community) Y2 Community is the public briefing catalog inside **InfoOps → Discover**. It combines profiles curated by Agent Y2 with profiles that users have marked **Share with Community**. This catalog is about reusable InfoOps profiles and their reports. It is separate from the “emergent signals” extracted inside reports and intelligence APIs. ## How community profiles work The profile remains owned by its creator. Following it creates an active subscription in your current workspace; it does not copy the profile or let you edit the creator's research settings. ## What you can do Browse global, city, industry, and user-published profiles; filter the catalog; and follow a briefing Make a profile and its generated reports discoverable to other Y2 users Review subscriptions and configure delivery from My Briefings Report a problem, improve documentation, or build against the documented API ## Catalog organization Discover groups public profiles using their stored flags and tags: | Browse group | Current rule | | --- | --- | | Featured | Agent Y2 global profile without a `city` or `industry` tag | | Cities | Any public profile tagged `city` | | Industries | Any public profile tagged `industry` | | Public | User-shared profile that is not classified as city or industry | These are browse rules, not editorial certifications. Tags influence where a profile appears. The catalog also supports search across name, topic, custom prompt, and tags; filters for public type, subscription status, profile status, tags, audio, and saved recursion depth; and sorting by recent delivery, name, creation date, or subscriber value. ## Subscription limits Subscriptions are counted in the active workspace. | Plan | Public-profile capacity | | --- | --- | | Free | 1 global or community subscription | | Lite | 3 global or community subscriptions | | Pro | Up to 20 total profiles, including up to 5 custom profiles and community subscriptions | | Elite | Up to 200 total profiles, including up to 50 custom profiles and community subscriptions | The subscription counts even if delivery is paused. Unfollow a profile from Discover or My Briefings to remove its subscription record and free capacity. ## Public-data boundary When a creator enables **Share with Community**, the profile becomes discoverable immediately; the current application path does not create a moderation or approval queue. Other signed-in users can inspect the profile topic, creator label, schedule and research configuration, subscriber and report counts, and available generated reports. Review the profile's topic, custom prompt, tags, configuration, and generated reports before sharing. Community visibility is a publication control, not private team sharing. ## Quality and reporting Y2 displays activity, report count, subscriber count, creator, tags, and configuration details, but the current code does not assign a “Verified Analyst” badge or guarantee content review. Evaluate a profile's topic and latest report before following it. To report inaccurate, inappropriate, or unsafe public content, email [support@y2.dev](mailto:support@y2.dev) with the profile name or URL and a concise description. ## Related guides Browse, filter, inspect, follow, and unfollow public briefing profiles Understand exactly what becomes public and how to turn sharing off Compare profile capacity and delivery features --- # Browse Public Briefings (/docs/community/signals) Use **InfoOps → Discover** to find public briefing profiles and follow them in your active workspace. The catalog includes Agent Y2 global profiles and profiles that users have enabled for community sharing. This page covers the briefing catalog. It is separate from the signal objects returned by the intelligence APIs. ## Before you follow a profile Check the workspace switcher first. A follow belongs to the active workspace and consumes that workspace's profile capacity; switching workspaces later does not move the subscription. | Plan | Capacity used by public briefings | | --- | --- | | Free | Up to 1 global or community subscription | | Lite | Up to 3 global or community subscriptions | | Pro | Subscription counts toward the 20-profile total | | Elite | Subscription counts toward the 200-profile total | Pausing delivery does not free capacity. Unfollow the profile to remove its subscription record. ## Find and follow a briefing ### Open Discover In the active workspace, open [InfoOps → Discover](/app/infoops/community). ### Choose a browse group Use **All**, **Featured**, **Cities**, **Industries**, or **Public**. With no search or tag filters, the All view shows grouped previews and up to 12 profiles per group. Choose **View All** when a group contains more results. ### Narrow the catalog Search by profile name, topic, custom instructions, or tags. Then combine the type, status, audio, saved depth, tag, and sort controls as needed. ### Inspect the profile Open the details view before following. Review the creator, topic, instructions, cadence, source and model configuration, activity status, latest report, and audio setting. Report and subscriber counts appear when available. ### Follow in the active workspace Select **Follow**. Y2 creates a workspace-scoped subscription to the original profile; it does not clone the profile or grant permission to edit it. ## How profiles are grouped Discover derives its groups from stored profile flags and tags. The labels are browse rules, not editorial certifications. | Group | Current catalog rule | | --- | --- | | Featured | Agent Y2 global profile without a `city` or `industry` tag | | Cities | Public profile tagged `city` | | Industries | Public profile tagged `industry` | | Public | User-shared profile not classified as city or industry | A `city` or `industry` tag takes precedence over the Featured grouping, even when the profile is also global. ## Search, filter, and sort | Control | Available behavior | | --- | --- | | Search | Matches name, topic, custom instructions, and tags; spaces, slashes, and hyphens are normalized for compact matching | | Type | All, Featured/global, or Public/user-shared | | Subscription | All, Following, or not currently followed through the status controls | | Profile status | All, Active, or Inactive | | Audio | Any, has audio enabled, or has no audio | | Saved depth | Quick for no saved depth or `0`; Standard for `1–2`; Deep for `3+` | | Tags | Matches a profile when it has at least one selected tag | | Sort | Alphabetical, most recent delivery, most followed, or recently created | The depth badge describes the profile's saved configuration. Report execution currently caps recursive research at depth 1; see [Research Workflows](/docs/workflows) for the runtime behavior. Subscriber counts are available in profile details, but the current catalog-list payload does not guarantee a count for every card. Profiles without a value can tie in **Most Followed** ordering, so inspect the profile and its latest report instead of treating the sort as a quality ranking. ## What following changes Following a profile: - Adds one subscription record to the active workspace. - Makes the profile available from **My Briefings**. - Preserves the creator's ownership and research configuration. - Lets the subscriber use delivery controls supported by their plan. - Receives later reports from the same shared profile while the subscription remains active. The creator can change the profile or stop sharing it. Existing subscription records are not automatically cloned or converted into workspace-owned profiles. To free capacity, choose **Following** in Discover or open **My Briefings**, then unfollow the profile. Turning a subscription inactive only pauses it and still counts toward the limit. ## App and API boundary Use the application to browse the public catalog and create or remove community subscriptions. The public [`GET /profiles`](/docs/api/reference/profiles/listProfiles) endpoint lists profile subscription records accessible to the authenticated workspace; it is not a public-catalog search endpoint. Use the generated [Profiles API reference](/docs/api/reference/profiles) for the current request, response, authentication, and scope contract. ## Troubleshooting Confirm the active workspace and its plan. Free and Lite have dedicated public-subscription limits; Pro and Elite combine owned profiles and followed profiles in one total. Unfollow an unused profile to free capacity. Clear the search, selected tags, and type or status filters. Then return to **All**. Also verify that you did not switch workspaces when checking which profiles are already followed. Inspect its details and latest report before following. The catalog can display inactive public profiles; visibility does not guarantee a future publishing cadence. Following references the creator's profile. Create a new workspace-owned profile when you need different instructions, sources, cadence, or sections. ## Related guides Review catalog visibility, quality signals, and subscription limits Publish a workspace-owned profile and understand what becomes visible Create and maintain workspace-owned and followed briefings Check profile, delivery, API, and workspace limits --- # Email Delivery (/docs/delivery/email) Email is the default delivery method for an Information Operations subscription. Y2 renders the generated report as an HTML email and sends it to the subscription owner's current account email. ## Configure email for a profile Delivery preferences belong to a profile subscription. Changing one profile does not change the others. ### Open My Profiles Go to **InfoOps → My Profiles**. ### Open Delivery Preferences Open the profile card's actions menu, then select **Delivery Preferences**. You can also find the same controls under **Advanced** when editing an existing profile. ### Select Email Only Choose **Email Only**. If a stored subscription has no delivery method, Y2 also treats it as email. ### Choose the audience Keep **Individual** to send to your account email. Eligible Elite workspaces can select **Workspace** to include workspace members. ### Save preferences Select **Save Preferences**. Saving also makes the subscription active. A new profile must be created before its subscription-level delivery preferences are available. Until then, the default method is email to the individual account. ## Verify or change the recipient email Email delivery uses the email stored on your Y2 account at the time the report is sent. 1. Open **Settings** in the app. 2. Find the **Email** account section. 3. Confirm the current verified address. 4. To change it, enter the new address and complete the emailed one-time-code flow. After verification, subsequent individual deliveries use the updated address. Y2 does not expose an arbitrary recipient-address field on each profile. ## Individual and workspace audiences | Audience | Availability | Recipients | | --- | --- | --- | | Individual | All plans | Subscription owner's current account email | | Workspace | Elite workspaces with member support | Owner plus eligible non-suspended members with account emails | The workspace audience is bounded by the workspace's member entitlement. A missing email on a member record prevents delivery to that member. Workspace delivery can expose the complete report to other members. Confirm the profile topic, report content, and workspace membership before enabling it. ```mermaid flowchart LR A[Scheduled profile] --> B[Report generated] B --> C{Delivery preference} C -->|Email| D[Account email] C -->|Email + SMS| D C -->|Elite workspace audience| E[Eligible workspace members] ``` ## Email contents The current report template can include: Profile name, topic, generated report body, and available intelligence sections. Sanitized citations from the report or a source-link section when separate citations are not already present. A link to the in-app report page when audio narration exists. Audio is not attached. Assigned logo, colors, terminology, and before/after static content when configured. The email subject is the profile name. The preview text uses the configured branding prefix plus that name. Report and branding HTML are sanitized before rendering. See [Branding Templates](/docs/profiles/branding) for the fields that can change email appearance and copy. ## Combine delivery channels The stored delivery method is one of four values: There is currently no combined Email + Webhook option. Select **Webhook** only when the endpoint should replace the email channel for that subscription. ## Delivery timing and history Y2 attempts delivery after report generation reaches the delivery stage. Generation duration, queueing, retries, and the email provider affect arrival time; there is no fixed five-minute delivery guarantee. Open **InfoOps → History** to inspect the recorded date, profile, status, method, and delivery details. A successful send request means Y2 queued or sent the message through its email provider; the recipient's provider can still filter, defer, bounce, or reject it. ## Troubleshoot missing email Confirm that the profile and subscription are active, then review its next scheduled run. A delivery cannot occur until report generation succeeds. Open **Delivery Preferences** and confirm that the method is **Email Only** or **Email + SMS**. **SMS Only** and **Webhook** do not send the report email. Open **Settings → Email**, complete verification if needed, and confirm the address before the next delivery. Review the delivery details, correct the account or workspace-member email, and wait for the next run. For persistent failures, contact [support@y2.dev](mailto:support@y2.dev). Check spam, quarantine, inbox rules, and the receiving provider's logs. Allow the exact sender shown in a received Y2 message; the configured sender address can vary by environment. ## Next steps Add a verified-phone summary channel on Pro or Elite. Replace email delivery with an authenticated endpoint. Understand when profile generation begins. --- # SMS Delivery (/docs/delivery/sms) SMS delivery sends an intelligence-report summary to the subscription owner's verified phone. It is available to active Pro and Elite workspaces. SMS requires **Pro or Elite**, a verified account phone, and a profile delivery preference set to **SMS Only** or **Email + SMS**. ```mermaid flowchart LR A[Verified account phone] --> B[Profile subscription] B --> C[Report generated] C --> D{SMS summary exists?} D -->|Yes| E[Use generated summary] D -->|No| F[Extract bounded report summary] E --> G[SMS provider] F --> G ``` ## Add and verify a phone ### Open account settings In the app, go to **Settings → Account** and find **Phone**. ### Enter the number Select a country code, enter the local number, and request a verification code. The current input supports common country-code presets and stores the combined international number. ### Verify ownership Enter the six-digit code sent to that phone. SMS delivery remains unavailable until the account has a phone verification timestamp. Use a number that can receive SMS. Standard carrier message and data rates may apply. ## Enable SMS for a profile ### Open Delivery Preferences Go to **InfoOps → My Profiles**, open the profile card's actions menu, and select **Delivery Preferences**. ### Choose a method Select **SMS Only** to omit email, or **Email + SMS** to receive both channels. ### Save Select **Save Preferences**. The backend rechecks the active workspace entitlement and phone verification before accepting an SMS method. | Method | Email | SMS | | --- | --- | --- | | SMS Only | No | Yes | | Email + SMS | Yes | Yes | SMS is sent only to the subscription owner's account phone. The Elite **Workspace** audience setting applies to email recipients, not to member phone numbers. ## Message contents Y2 first uses the report's pre-generated `smsSummary` when present. If a report does not have one, the delivery path extracts a concise section from the HTML report and prefixes it with the profile name. ```text Y2 Cyber Risk: Critical infrastructure advisories increased across the monitored sources... ``` The fallback formatter targets a 140-character message and truncates its extracted summary when needed. Generated `smsSummary` content is also instructed to be concise, but carrier segmentation can vary with message length and character encoding. The current SMS template does not guarantee a link to the full report. Open Y2 directly when you need the report body, citations, or audio. ## Timing, failures, and limits SMS is attempted after report generation enters the delivery stage. When **Email + SMS** is selected, the current workflow attempts email before SMS. Queueing and provider response times vary, so there is no fixed one-minute arrival guarantee. The send provider can reject a request for reasons including rate limits, an invalid number or message, authentication, or service availability. Y2 records SMS success or failure in **InfoOps → History** when the provider call reaches the SMS delivery action. Y2 does not currently document a fixed per-account daily SMS quota. Plan access and provider limits still apply. ## Change or remove the phone Open **Settings → Account → Phone** to change or remove the verified number. - Changing the number requires a new one-time-code verification. - Removing it clears the phone and verification timestamp from the account. - Removing a phone prevents later SMS delivery, but it does not rewrite every stored profile preference. Change affected profiles to **Email Only** if you still want delivery. The SMS provider necessarily receives the destination number and message content to deliver the text. Do not place secrets or highly sensitive material in profile instructions that can appear in a report summary. ## Troubleshoot SMS delivery Confirm that the active workspace is Pro or Elite and that **Settings → Account → Phone** shows a verified number. Confirm the selected country code and local number, then retry. Verification requests can fail when the SMS provider is unavailable or the number is already associated with another account. Verify that the profile uses **Email + SMS**, not **Email Only**. Then review **InfoOps → History** for an SMS-specific failure. Add and verify a phone again, then reopen Delivery Preferences. Stored SMS selections cannot deliver without a current verified account phone. Check the History details and retry on the next report run. For persistent authentication or service failures, contact [support@y2.dev](mailto:support@y2.dev). ## Next steps Receive the complete HTML report and source links. Deliver structured events to an endpoint instead of email or SMS. Compare delivery features across workspace plans. --- # Webhook Delivery (/docs/delivery/webhooks) Webhook delivery sends a signed CloudEvents 1.0 notification to an HTTPS endpoint after a report is published. It is available to active Lite, Pro, and Elite workspaces. ```mermaid flowchart LR A[Report published] --> B[CloudEvent persisted] B --> C[Signed POST] C --> D{Endpoint returns 2xx?} D -->|Yes| E[Success recorded] D -->|No| F[Failure count incremented] F --> G[Disabled after 5 consecutive failures] ``` A webhook notification is compact. It identifies the report and provides API links; it does not embed the full HTML report or all source records. ## Create an endpoint in Y2 You do not need an API key when configuring webhooks in the app. ### Open Webhooks In an active paid workspace, go to **Developers → Webhooks**. ### Create a configuration Enter a name and public endpoint URL. Production webhook URLs must use HTTPS. Localhost, loopback, private-network, link-local, and other blocked addresses are rejected. ### Add verification and authentication Generate or enter an optional signing secret. You can also add custom string headers, such as an authorization token for your endpoint. `Host`, `Content-Length`, and `Content-Type` cannot be supplied as custom headers. Y2's own delivery headers take precedence over custom values. ### Save and test Create the webhook, then select **Test Webhook**. The test sends a `dev.y2.webhook.test.v1` CloudEvent and waits up to 10 seconds for a response. ### Assign it to a profile Go to **InfoOps → My Profiles → Delivery Preferences**, choose **Webhook**, select the active configuration, and save. Selecting **Webhook** replaces email and SMS for that subscription. There is no combined Email + Webhook delivery method. ## Receive a report event Report deliveries use `Content-Type: application/cloudevents+json` and the event type `dev.y2.report.generated.v1`. ```json { "specversion": "1.0", "id": "y2mexampleeventid", "source": "https://api.y2.dev/api/v1/profiles/prf_0123456789abcdef01234567", "type": "dev.y2.report.generated.v1", "subject": "reports/rpt_0123456789abcdef01234567", "time": "2026-07-21T12:00:00.000Z", "datacontenttype": "application/json", "dataschema": "https://api.y2.dev/schemas/events/report-generated-v1.json", "data": { "report": { "id": "rpt_0123456789abcdef01234567", "profileId": "prf_0123456789abcdef01234567", "summary": "Concise report summary...", "status": "published", "generatedAt": "2026-07-21T11:59:48.000Z", "language": "en", "intelligence": { "signalCount": 4, "graphNodeCount": 12 }, "audio": { "status": "available", "durationSeconds": 318 } }, "subscription": { "id": "sub_0123456789abcdef01234567" }, "links": { "report": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567", "markdown": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?format=markdown", "sources": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?include=sources", "signals": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/signals", "graph": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/graph", "audio": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/audio" } } } ``` `summary` can be `null`, and audio can be marked `unavailable`. Following a link can require an appropriately scoped API key or the endpoint's documented x402 flow. ## Request headers "', }, }} /> ## Verify the signature Compute HMAC-SHA256 over the exact raw request body bytes using the configured secret. Prefix the hex digest with `sha256=` and compare it with `X-Y2-Signature` in constant time. ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyY2Webhook(rawBody, headers, secret) { const supplied = headers["x-y2-signature"] ?? ""; const timestamp = Number(headers["x-y2-timestamp"] ?? 0); const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - timestamp); if (!timestamp || ageSeconds > 300) return false; const expected = `sha256=${createHmac("sha256", secret) .update(rawBody) .digest("hex")}`; const suppliedBytes = Buffer.from(supplied); const expectedBytes = Buffer.from(expected); return ( suppliedBytes.length === expectedBytes.length && timingSafeEqual(suppliedBytes, expectedBytes) ); } ``` Use the raw bytes before JSON parsing; re-serializing an object can change the signed body. Keep your own replay window policy and deduplicate with `Idempotency-Key` or `X-Y2-Event-Id`. A secret is optional in the configuration form. If you omit it, Y2 does not send `X-Y2-Signature`; secure the endpoint with a custom authorization header or another control. ## Acknowledge deliveries safely The production delivery request has a 10-second timeout. Return a `2xx` response only after the event is durably accepted, then process long-running work asynchronously. ```javascript app.post("/y2/webhook", rawBodyMiddleware, async (request, response) => { if (!verifyY2Webhook(request.rawBody, request.headers, process.env.Y2_WEBHOOK_SECRET)) { return response.sendStatus(401); } await queue.put({ id: request.headers["x-y2-event-id"], event: JSON.parse(request.rawBody.toString("utf8")), }); return response.sendStatus(202); }); ``` Temporary failures (network errors, timeouts, HTTP 408/429, and 5xx responses) are retried up to five total attempts per event, with delays of 5 seconds, 30 seconds, 5 minutes, and 30 minutes. Other HTTP rejections pause the destination immediately. Five consecutive failures also pause it; a successful delivery resets the failure count. Retrying delivery does not regenerate reports. The configuration owner receives one warning email when temporary failures begin and one email when delivery is paused. Reports, configuration, and delivery history are retained. Other delivery destinations continue independently. To recover a paused destination, save any endpoint, secret, or header changes, run **Test Webhook**, then enable delivery within 15 minutes. Tests work while paused; changing the configuration or a new production failure invalidates the test. Use **Delivery history and replay** to resend a retained failed event after enabling. Replay preserves its original payload and event ID so the receiver can deduplicate it. Enabling does not automatically send the backlog. ## Manage configurations - Editing a masked webhook leaves its existing secret unchanged when the secret field is blank. - Activating a paused webhook requires a current successful test and resets its failure count. - An inactive webhook cannot be assigned for delivery. - A configuration cannot be deleted while any subscription references it. Reassign those profiles first. - The app exposes 30-day success/failure statistics, last-use time, subscription count, and current consecutive failures. You can also manage configurations through the public API. API-key requests use the `webhooks:manage` scope; creating new API keys requires Pro or Elite, while webhook delivery itself is available on Lite. ## Next steps Review management endpoints and integration boundaries. Inspect canonical request and response schemas. Retrieve report content linked by an event. --- # Getting Started (/docs) Set up Y2, choose what you want to monitor, and confirm where your intelligence reports will arrive. In about 10 minutes, you will have an active workspace, at least one intelligence profile, and a delivery path you can verify in **My Profiles**. ## How Y2 works An intelligence profile defines the subject Y2 monitors. Y2 runs research for that profile and makes each completed report available in the app and through the delivery methods included with your workspace plan. ## Before you begin You need an email address or phone number that can receive a verification code. No API key is required for this tutorial. For Mac and Windows, see [desktop download availability](/download) and the [installation guide](/docs/apps). Y2 Desktop is in beta; check the download page for published installers. A Free workspace can subscribe to one community profile. Custom-profile creation and delivery start with Lite. Pro adds scheduled-report management, SMS, audio, and new API keys. See [Plans and limits](/docs/plans) for the complete reference. ## Set up your first intelligence workflow ### Sign in and finish onboarding Open [Y2 sign in](/login), choose email or phone verification, and enter the code you receive. Complete the workspace prompts before continuing. Your setup is ready when Y2 opens your workspace dashboard. ### Choose how to start Choose an existing briefing when you want a ready-made topic. This is the fastest path and is available on Free. Define your own topic, keywords, cadence, and delivery settings. Free includes one trial profile; ongoing custom-profile delivery starts with Lite. ### Configure the profile For a community profile, review its topic and cadence, then confirm the subscription. For a custom profile, enter a clear name and topic, add useful keywords, and choose a frequency. Start with a narrow subject you can recognize in the resulting report. The [profile creation guide](/docs/profiles/creating) explains every field. ### Choose an available delivery method Email is the default. Webhook delivery requires Lite or higher; SMS requires Pro or Elite. You can change delivery settings later from the profile. Receive the formatted report at the verified email address. Send report JSON to an HTTPS endpoint on Lite, Pro, or Elite. Receive a concise mobile summary on Pro or Elite. ### Confirm the setup Open [My Profiles](/app/infoops/my-profiles). Your new profile or subscription should appear in the list with its current status and delivery method. The setup is complete when you can open the profile from this page. Custom reports follow the configured cadence; completed reports remain available from the profile. ## Choose your next path Learn how topics, schedules, sharing, branding, and report delivery fit together. Use the Situation Room, source health, country cards, and investigation surfaces. Authenticate, test requests in the Workbench, and use the generated endpoint reference. Give an agent the contract, documentation bundle, and repository-specific integration prompt. ## Understand the platform - [Mission](/docs/mission) explains why Y2 makes intelligence workflows broadly accessible. - [Vision](/docs/vision) describes the direction of the platform. - [Plans and limits](/docs/plans) lists current workspace capabilities and quotas. - For help with your account, email [support@y2.dev](mailto:support@y2.dev). --- # LLM-friendly Documentation (/docs/llms) Y2 publishes build-generated text and MDX artifacts so an AI integration can discover, retrieve, and cite the same documentation shown in Fumadocs. Start with `/llms.txt`. Fetch one page when the task is narrow, use `/llms-full.txt` only when broad product context is necessary, and use `/api/openapi.yaml` for request and response schemas. ## How the artifacts are produced ```mermaid flowchart LR source["content/docs MDX"] --> index["/llms.txt"] source --> full["/llms-full.txt"] source --> page["/docs/{slug}.mdx"] source --> search["Fumadocs search index"] openapi["public/api/openapi.yaml"] --> reference["Generated endpoint reference"] ``` The Vite documentation plugin reads `content/docs` during a production build. It emits the index, full corpus, page-level MDX files, and the Fumadocs search index. During local development, the same plugin generates these responses on request. The OpenAPI reference is generated separately from `public/api/openapi.yaml`. ## Artifact reference | Artifact | Content | Use it for | |---|---|---| | `/llms.txt` | Plain-text overview and grouped links to every documentation page | Discovery and routing | | `/llms-full.txt` | Concatenated page titles, canonical paths, and source MDX bodies | Broad grounding or corpus ingestion | | `/docs.mdx` | Source MDX body for the documentation home page | Targeted retrieval | | `/docs/{slug}.mdx` | Source MDX body for one documentation page | Task-specific context | | `/api/openapi.yaml` | OpenAPI contract for public HTTP endpoints | Types, parameters, schemas, scopes, and responses | These are public documentation artifacts and do not require a Y2 API key. Page-level files and `/llms-full.txt` contain the MDX body without frontmatter. Fumadocs tags such as `Callout`, `Cards`, and `APIPage` can remain in the text. Generated endpoint pages may point to `public/api/openapi.yaml` instead of expanding every schema, so API clients must also read the published OpenAPI contract. ## Retrieve documentation ```bash curl -fsS https://y2.dev/llms.txt ``` Use the grouped links and descriptions to decide which page the task needs. ```bash curl -fsS https://y2.dev/docs/api/authentication.mdx ``` Append `.mdx` to a canonical documentation path. For example, `/docs/api` becomes `/docs/api.mdx` and `/docs/profiles/creating` becomes `/docs/profiles/creating.mdx`. ```bash curl -fsS https://y2.dev/llms-full.txt ``` Use this response when the agent needs relationships across several documentation sections. ```bash curl -fsS https://y2.dev/api/openapi.yaml ``` Treat the operation definitions and schemas here as authoritative for HTTP integration code. The deployed files use revalidation caching. Re-fetch them when starting a new integration task or after a Y2 release instead of treating a local copy as permanent. ## Ingest the full corpus safely Each page in `/llms-full.txt` begins with a generated header: ```text # Page Title (/docs/canonical-path) [Source MDX body] ``` The plugin places `---` between pages, but source pages can also contain their own horizontal rules. Do not split on every `---`. Detect generated page headers that match a title followed by a `/docs` path, and retain that path as source metadata. ### Fetch and validate the response Require a successful HTTP response and a text content type before replacing an existing corpus. ### Detect page boundaries Start a new document at headers shaped like `# Title (/docs/path)`. Keep the title and path separate from the MDX body. ### Chunk within each page Split long pages by their Markdown headings while preserving the canonical page path on every chunk. Keep code blocks intact. ### Index the API contract separately Parse `/api/openapi.yaml` by operation and schema. Do not infer endpoint shapes from examples or from an `APIPage` component tag in the MDX corpus. ## Use MCP when the agent needs tools The LLM artifacts provide documentation context only. Use the Y2 MCP integration when an agent also needs bounded report or News reads, OpenAPI operation lookup, or Agent Y2 access. `@y2-intel/mcp` is a separate package whose implementation is not in this platform repository. After installation, use the MCP client's `tools/list`, `resources/list`, and `prompts/list` responses as the source of truth for that installed version. The [MCP guide](/docs/api/mcp) covers client configuration, scoped credentials, capability inspection, and troubleshooting. Keep `Y2_API_KEY` in the MCP process environment, never in a prompt or tool argument. ## Prepare a coding agent Use the maintained integration prompt when an agent will write Y2 API code. It directs the agent to the documentation index, OpenAPI contract, current base URLs, authentication rules, and plan boundaries. Ground an agent, constrain its scope, and verify the generated integration. Choose the correct server, validate the contract, and regenerate client types. Configure and inspect the separately distributed MCP server. Build server-side TypeScript, Python, or command-line integrations from the contract. --- # Mission (/docs/mission) Y2 exists to make useful intelligence accessible to people and organizations that do not have a dedicated research team. ## The problem Important signals are scattered across feeds, reports, markets, maps, and specialized data providers. Following them manually creates three recurring problems: - **Coverage gaps:** no single source contains the full picture. - **Attention limits:** people cannot continuously monitor every relevant source. - **Lost context:** one-off searches rarely preserve what changed or why it matters. Traditional intelligence services can address these problems, but their cost and operating model often put them out of reach for individuals and smaller teams. ## Y2's approach Profiles turn a recurring question into an intelligence workflow with a defined topic, research cadence, and delivery path. Live feeds, reports, entities, incidents, and relationships remain connected so readers can move from an alert to the supporting context. Intelligence can remain in the workspace or move through email, SMS, webhooks, audio, and the API when the active plan supports them. Global and community profiles let one well-designed monitoring workflow benefit more than one person. ## Operating principles ### User-defined priorities The operator decides what to monitor, which sources and workflows are relevant, and where results should go. Automation serves that intent rather than replacing it. ### Traceable evidence Reports and intelligence objects should retain enough source and provenance context for readers to inspect the underlying evidence and form their own judgment. ### Human judgment remains final Y2 can collect, organize, and synthesize information. It does not remove uncertainty or make a decision on the operator's behalf. ### Broad access The platform should support an individual testing one briefing, an analyst building a repeatable workflow, and a team operating shared intelligence resources. Open-source data can be incomplete, delayed, duplicated, or wrong. Treat generated analysis as decision support, review cited evidence, and apply appropriate professional judgment. ## Who Y2 serves Y2 is designed for analysts, investors, security professionals, researchers, operators, and business leaders who need repeatable awareness of complex or fast-changing topics. ## Put the mission into practice Set up a workspace and verify your first intelligence profile. Explore live events, sources, country context, and connected intelligence. Bring Y2 intelligence into an application or operational workflow. --- # News Signal Feed (/docs/news) The News Signal Feed lets signed-in users monitor cached RSS and X signals across 40 topics. Select the topics relevant to your work, then switch between individual signals and AI-generated 12-hour recaps. “Live Feed” describes the newest signals available in Y2's cache. RSS sources and recaps are refreshed on scheduled cycles; X source ingestion currently runs on a separate weekly cycle. ## Monitor selected topics ### Open News Sign in to Y2 and select **News** in the app navigation. ### Select feeds Choose one or more chips under **Global Signal Feeds**. The current catalog contains 40 feeds organized into six groups: | Group | Examples | | --- | --- | | Politics & Geopolitics | Geopolitics, US Politics, Defense | | Macro & Markets | Macro, Equities, Rates & FX | | Industry Verticals | Energy, Cyber, Biotech & Health | | Regional Desks | MENA, Europe, Asia-Pacific | | AI & Technology | AI, AI Agents, Machine Learning | | Crypto & Digital Assets | Bitcoin, Ethereum, DeFi | See [Topics and feeds](/docs/news/topics) for the complete catalog. ### Choose a view Select **Live Feed** for individual signals or **12h Recap** for a per-topic synthesis. ### Open a source When a signal has a source URL, select the row or **View Source** to open the original item in a new tab. Your selected feeds and view mode are saved to your account. Feed changes are written after a short debounce, so the same selection can be restored on another signed-in device. ## Read a signal Each Live Feed row can show: Sentiment is a directional signal, not a confidence score or investment recommendation. Items without a supported classification appear without a sentiment badge. Treat generated sentiment and recaps as triage aids. Open the attributed source and verify material claims before acting on them. ## Use 12-hour recaps Select **12h Recap** to read the current cached recap for each selected feed. A recap card shows the topic, creation time when available, and generated recap content. The app currently exposes only the 12-hour view. The backend and public API accept additional timeframe identifiers, but they are not selectable in the News interface and may not have cached content for every topic. See [AI news recaps](/docs/news/recaps) for interpretation and availability details. ## Refresh and stale data The **Refresh** button re-runs the active Live Feed or Recap query. It does not start a new RSS or X ingestion job. If the upstream cache has not changed, the same results remain visible. Y2 displays a stale-data notice when the current query result is old. If no results appear: 1. Confirm that at least one feed is selected. 2. Try a broader set of feeds. 3. Select **Refresh** to re-read the cache. 4. If the cache is initializing, wait for the scheduled sync and try again. ## News feeds and country cards The topic feed and the Situation Room's country cards answer different questions. News shows cached items for your selected topics. Eligible items from ontology-enabled feeds can also become observations linked to canonical entities and places. Country-card news selects observations attributed to the chosen country, along with other supported news and report sources. A topic item can therefore appear in News without appearing on a country card: it may lack a supported country attribution, have expired, or not qualify for the observation feed. Regional relevance and a country mentioned in a summary do not by themselves establish the event's location. For Red Sea coverage, explore MENA, Geopolitics, Logistics, and Energy topics as well as the relevant [country cards](/docs/osint/country-cards). Opening a card reads stored observations; it does not start a fresh news search. ## API access The public API exposes cached signals, recaps, and the feed catalog: Filter signals by topic and paginate results. Retrieve cached topic recaps. Discover available topic identifiers. API-key requests require the `news:read` scope. New API keys require Pro or Elite; eligible existing Lite keys retain their historical grace limits. These endpoints also support the x402 flow described in their generated references. ## Next steps Review the complete feed catalog and topic identifiers. Learn what the 12-hour synthesis contains and how to use it. Configure an API key and scopes. --- # AI News Recaps (/docs/news/recaps) AI News Recaps condense recent signals for each selected topic into a cached narrative. Use them to establish context quickly, then open the underlying Live Feed sources before making a decision. ```mermaid flowchart LR A[Topic cache] --> B[12h window] B --> C[Up to 24 recent signals] C --> D{LLM recap available?} D -->|Yes| E[Structured recap] D -->|No or failed| F[Deterministic fallback] E --> G[Recap cache] F --> G G --> H[News interface] G --> I[Public API] ``` ## Read recaps in the app ### Open News Select **News** in the signed-in app. ### Select topics Choose one or more feeds under **Global Signal Feeds**. ### Switch to 12h Recap Select **12h Recap** in the page header. Each selected topic with cached content receives a separate card. ### Verify important signals Return to **Live Feed** and open attributed sources for any claim that affects your work. The dashboard currently exposes only the 12-hour recap view. All 40 topics in the current feed registry are configured for that window. ## Generation schedule RSS ingestion starts at 00:00, 06:00, 12:00, and 18:00 UTC. Recap scheduling begins two minutes after each of those cycles, then staggers topic jobs by 45 seconds to avoid starting all 40 at once. Recaps are pre-generated and cached. Opening the page or selecting **Refresh** re-reads the cache; it does not generate a new recap on demand. X ingestion currently runs on a separate weekly schedule. A recap can therefore combine recently cached RSS signals with X signals captured during the latest X cycle. ## Recap contents When structured generation succeeds, the cached record can contain: The app prioritizes the `recap` narrative. The API returns the complete cached object for each topic, so integrations should tolerate additional fields and the fallback shape. ### Fallback output If structured LLM generation is unavailable or fails, Y2 creates a deterministic recap from the recent cached signals. The fallback can include the narrative, item and source counts, source mix, and top signals. This keeps the topic cache usable without presenting failed model output as a successful structured result. ## Retrieve recaps with the API ```bash curl "https://api.y2.dev/api/v1/news/recaps?topics=bitcoin,ethereum&timeframe=12h" \ -H "Authorization: Bearer y2_your_api_key" ``` The response maps each available topic ID to its cached recap: ```json { "data": { "bitcoin": { "recap": "Bitcoin 12-hour recap content...", "executive_summary": "The highest-signal developments...", "sentiment": "neutral", "item_count": 12, "created_at": "2026-07-21T12:18:00.000Z" } }, "meta": { "timeframe": "12h", "topics": ["bitcoin", "ethereum"] } } ``` The API schema accepts `12h`, `24h`, `3d`, and `7d`, but the current registry schedules only `12h` cache entries. A valid longer timeframe can therefore return no recap for a topic. See the generated [List recaps reference](/docs/api/reference/news/listRecaps) for the canonical authentication, query, and response schema. ## Interpret recaps safely A recap is generated from a bounded set of cached signals. It may omit events, repeat source errors, or infer a misleading emphasis. It is not a substitute for source review or professional financial, legal, security, or policy advice. - A missing topic card can mean that no successful recap is cached for that selection. - Quiet topics can produce a neutral “no direct signals” message. - `item_count` describes the items considered by that recap, not total market activity. - Sentiment summarizes the supplied signals; it does not forecast price or outcome. - Source IDs in structured fields identify inputs, but you should use Live Feed source links for direct verification. ## Next steps Inspect individual signals and open their sources. Choose feeds for the app or API. Review authentication and the canonical response schema. --- # Topics and Feeds (/docs/news/topics) Y2 currently defines 40 News feeds in a shared source registry. The app and API use the IDs on this page to select topic caches. The feed catalog is configuration-driven. Use `GET /api/v1/news/feeds` when an integration needs to discover the current list at runtime instead of storing a copy. ## Feed catalog | Feed | ID | Coverage | | --- | --- | --- | | Geopolitics | `geopolitics` | Alliances, wars, sanctions, and diplomatic flashpoints | | US Politics | `politics_us` | Federal politics, elections, Congress, and major policy shifts | | Defense | `defense` | Military operations, NATO, weapons programs, and security policy | | Sanctions & Trade | `sanctions_trade` | Export controls, tariffs, sanctions, and industrial policy | | Feed | ID | Coverage | | --- | --- | --- | | Macro | `macro` | High-impact macroeconomic and geopolitical events with market relevance | | Equities | `equities` | Global equity markets, earnings, indices, and market structure | | Rates & FX | `rates_fx` | Interest rates, currencies, central banks, and yield curves | | Commodities | `commodities` | Energy, metals, agriculture, and commodity-market shocks | | Banking & Regulation | `banking_reg` | Banking stress, financial regulation, and financial stability | | Feed | ID | Coverage | | --- | --- | --- | | Energy | `energy` | Oil and gas, power markets, grids, nuclear, and renewables | | Cyber | `cyber` | Attacks, ransomware, vulnerabilities, infrastructure, and threat actors | | Semiconductors | `semiconductors` | Chip manufacturing, foundries, accelerators, and supply chains | | Biotech & Health | `biotech` | Pharma, biotech, health alerts, and life-science market events | | Logistics & Maritime | `logistics` | Ports, shipping, chokepoints, supply chains, and disruptions | | Critical Minerals | `critical_minerals` | Rare earths, lithium, copper, mining, and battery materials | | Telecom | `telecom` | 5G, submarine cables, satellite broadband, and network infrastructure | | Feed | ID | Coverage | | --- | --- | --- | | MENA | `region_mena` | Middle East and North Africa political, conflict, and economic signals | | Europe | `region_europe` | European politics, EU policy, security, and regional economics | | Asia-Pacific | `region_asiapac` | China, Indo-Pacific security, ASEAN, and regional political economy | | Latin America | `region_latam` | Politics, security, crime, and economic signals in Latin America | | Africa | `region_africa` | Politics, conflict, resources, and regional economic developments | | North America | `region_namerica` | US, Canada, and Mexico cross-border political and economic signals | | Feed | ID | Coverage | | --- | --- | --- | | AI | `ai` | LLMs, infrastructure, model launches, and major partnerships | | AI Agents | `ai_agents` | Agent frameworks, incentives, ecosystems, and launches | | Data | `dats` | Data, analytics, and infrastructure developments | | Machine Learning | `machine_learning` | Academic, industry, and open-source ML developments | | Tech | `tech` | Software, semiconductors, regulation, and AI infrastructure | | Feed | ID | Coverage | | --- | --- | --- | | Base | `base` | Base L2 network and ecosystem developments | | Bitcoin | `bitcoin` | Network upgrades, ETF flows, miners, and adoption | | Crypto | `crypto` | Broad markets, launches, infrastructure, and protocols | | DeFi | `defi` | Protocols, yield markets, governance, and infrastructure | | Ethereum | `ethereum` | Protocol upgrades, scaling, validators, and ecosystem changes | | Hyperliquid | `hyperliquid` | HYPE, governance, perps liquidity, and ecosystem projects | | On-Chain Whales | `on_chain_whale` | Large transactions, wallets, and whale activity | | Perps | `perps` | Perpetual futures, derivatives, leverage, and trading platforms | | Ripple | `ripple` | XRP, XRPL, Ripple Labs, and related legal developments | | RWA | `rwa` | Tokenized real-world assets, issuers, platforms, and regulation | | Solana | `solana` | Protocol, validator, outage, and core-network developments | | Token Listings | `token_listings` | Exchange additions, token listings, and launches | | Virtuals | `virtuals` | Virtuals Protocol, GAME, agents, commerce, and tokenomics | Every topic in the current registry uses a 12-hour recap window. ## Select feeds in the app ### Open News Go to **News** and find **Global Signal Feeds**. ### Toggle topic chips Select a chip to add that feed. Select it again to remove it. You can combine topics from any group. ### Allow the preference to save Feed changes are saved after a one-second debounce. The app restores the saved selection on later visits. The dashboard does not apply the API's default topic list to your saved preferences. If no feeds are selected, choose at least one chip before the Live Feed or Recap view can load data. ## Choose a useful combination Start narrow, then add broader feeds only when they contribute different coverage. `macro`, `equities`, `rates_fx`, `geopolitics` `ai`, `semiconductors`, `critical_minerals`, `energy` `cyber`, `defense`, `telecom`, `geopolitics` `crypto`, `bitcoin`, `ethereum`, `defi` Broad and narrow topics can overlap. For example, `crypto` can include events also found under `bitcoin` or `ethereum`. More feeds increase coverage and can also increase duplicate context. ## Use topic IDs with the API Pass a comma-separated list to the `topics` query parameter: ```bash curl "https://api.y2.dev/api/v1/news?topics=geopolitics,macro,energy&limit=50" \ -H "Authorization: Bearer y2_your_api_key" ``` If `topics` is omitted, the API currently uses: ```text crypto, geopolitics, macro, equities, ai, energy ``` Unknown topic IDs return `400 Bad Request`. ### Discover feeds dynamically ```bash curl "https://api.y2.dev/api/v1/news/feeds" \ -H "Authorization: Bearer y2_your_api_key" ``` Each feed entry includes its `id`, display names, description, group metadata, UI color, and ontology-ingestion flag. Response metadata includes the catalog count and API default topics. See the generated [List feeds reference](/docs/api/reference/news/listFeeds) for the canonical response schema and authentication options. ## Next steps Filter current signals and open attributed sources. Understand recap availability and limitations. Review pagination, filters, and response fields. --- # Catalog and Ledger Alignment (/docs/ontology/explanation/catalog-alignment) Y2 has two ontology layers with different jobs. | | Shared catalog | Workspace ledger | | --- | --- | --- | | Holds | One entity per real-world thing, with names, aliases, external IDs, and relations | Evidenced claims about subjects your workspace researches | | Built by | Ingestion pipelines, extraction, and enrichment across Y2 | Analysts and integrations in your workspace | | Identity | Resolved: records that share a name or alias within a kind resolve to one entity | Never merged on a shared string; `same-as` stays a hypothesis | | Changes | Updated in place; merges leave redirects | Append-only; every correction is a new line | | Grades | A confidence on each relation | A verification state and confidence on each claim, tied to cited observations | | Visibility | Shared across Y2 | Private to the workspace | The catalog is a resolved projection that makes search, graphs, and fusion fast. The ledger is the claim store that makes research accountable. Neither replaces the other. ## Anchors connect them A ledger subject can be **anchored** to a catalog entity. The anchor is an index between the layers: it is not a claim and not a merge. Two subjects may share one anchor, for example while a `same-as` hypothesis is contested. When catalog entities merge, anchors follow the surviving entity. Anchoring respects each layer's classes: | Catalog kind | Ledger class | | --- | --- | | `person` | `person` | | `organization`, `vendor` | `organization` (a vendor is an organization in a market role) | | `threat_actor` | `organization` or `person`; the actor assessment stays in the catalog | | `software`, `api_service`, `ai_model` | `system` | | `cve`, `malware_family`, `indicator` | Not anchored: vulnerability and adversary-behavior content stays in the catalog | | `country`, `region`, `facility`, `vessel`, `aircraft`, `asset`, `protocol` | Not anchored: places, tracked assets, and standards are not passive-research subjects | ## Relations map where the meaning matches | Catalog relation | Ledger predicate | Used by fusion as | | --- | --- | --- | | `supplies` | `supplies` | Supplier or customer path | | `subsidiary_of` | `subsidiary-of` | Parent or subsidiary path | | `operates` | `operates` | Operated-system path | | `part_of` | `affiliated-with` (the catalog tie carries no role) | Not a path | | `affects`, `targets`, `uses` | None: vulnerability and adversary behavior | Cyber exposure events | | `ally_of`, `adversary_of`, `located_in` | None: geopolitical assessments and geography | Not a path | | `references` | None: a document reference | Not a path | ## Catalog identifiers are candidates, not claims The catalog may already hold a company's Wikidata ID, ASN, website, and social profiles. Those are useful starting points, but they arrive without the retrieval, excerpt, and verification state a ledger claim needs, so Y2 never copies them into the ledger as claims. Record them yourself with the page that supports them. The catalog's combined CIK-or-ticker field is never guessed into either namespace: record a `sec-cik` or a `ticker` designator from the filing or exchange page. ## Why keep them separate Resolving identity in the catalog is the right trade-off for a shared index of well-known organizations, CVEs, and places: it keeps graphs connected and search fast. The same rule is the wrong one for research about people and less-known parties, where two records that share a name are often two different people. Keeping the ledger separate lets each layer use the identity rule that fits its job, while anchors let fusion read both. --- # Why a Claim Ledger (/docs/ontology/explanation/claim-ledger) Most knowledge graphs store the current answer: one node per thing, properties overwritten as new data arrives, and a merge whenever two records look alike. That shape is fast to query and easy to get wrong in ways you cannot see later. Y2's entity ledger stores something different: what a public source said, when it was retrieved, how strongly it supports a claim, and when that claim is taken to hold. The picture you read is computed from those claims. ## The subject of a claim is not its string A person, organization, or system is a **subject**: a row opened with a class and nothing else. Everything that can change arrives as a **claim** about it. A name, a handle, a domain, or a CIK is a **designator**: one public string, stored once. Keeping the string and the subject apart is what prevents the most common identity error in collected intelligence. Two people who share a published name, or two companies whose aliases overlap, are two subjects that happen to be designated by one string. Graph platforms that derive an entity's identity from its name and aliases collapse them. In the ledger, a shared designator is only a shared string. Subjects merge only by a human decision, made by reading a `same-as` hypothesis that the ledger still holds as a hypothesis; when a `distinct-from` is also live, the pair is contested and both rows remain. ## Every change is a new line Claims are appended, never edited. A correction supersedes the current head of its claim key; a retraction clears the grade and interval but still cites the observation that says why. Each line is sealed to the one before it with SHA-256, so an edited history fails verification. This costs some storage and buys three things: - **Accountability.** You can show what you believed, on what evidence, before a correction. - **Safe collaboration.** A line must name the head it replaces, so two analysts working from stale views cannot silently overwrite each other; the second write is rejected. - **Portability.** The canonical log is a plain NDJSON file that anyone can check with the reference checker, independent of Y2. ## Verification belongs to the claim A person is not "verified." A specific handle claim is `located` because one page ties it, or `cross-linked` because two different pages identify it. The ledger enforces that the state matches the evidence: `cross-linked` needs two retrieved pages; `analytic` means the only support is an analyst's note, which is how hypotheses and display labels are graded. This follows the Berkeley Protocol's separation of provenance, source evaluation, and verification, and keeps the grade on the thing that was actually checked. ## Three clocks | Clock | Field | Question | | --- | --- | --- | | Record time | `recorded_at` | What had the ledger written by then? | | Valid time | `valid_from`, `valid_to` | When is the claim taken to hold in the world? | | Retrieval time | `retrieved_at` | When was the page fetched? | Record time selects a prefix of the log: what the research believed at that moment. Valid time filters the heads of that prefix. Asking "what did we believe on March 20 about April 10" sets both. A missing bound means unknown, never a guessed date: a page that says "since 2023" is quoted in the excerpt until a source gives a day. When the only evidence is that a page showed something between two retrievals, the interval is a sighting interval and the note says so. ## Passive by construction The method vocabulary is closed and public: web pages, profiles, DNS, certificate transparency, archives, registries, code hosts, and analyst notes that cite them. Interaction with the subject, authentication, and non-public access have no name in the vocabulary, so they cannot be recorded as if they were valid sources. The ledger keeps a short excerpt and its hash, not page bodies, and Y2 never writes a personal name into a sealed line. ## Where the design comes from The ledger borrows its publishing pattern from MITRE ATT&CK (stable IDs, every statement sourced), its interchange from STIX 2.1 (identities, observables, relationships with confidence and time bounds), its provenance fields from W3C PROV-O, and its method duties from the Berkeley Protocol. It deliberately differs from graph platforms that upsert on a deterministic ID and deduplicate nearby edges: the ledger keeps every correction. See [STIX 2.1 export](/docs/ontology/reference/stix-export) for the projection, and [Catalog and ledger alignment](/docs/ontology/explanation/catalog-alignment) for how the ledger relates to Y2's shared catalog. --- # Cyber-Market Fusion (/docs/ontology/explanation/cyber-market-fusion) Supply chains, geopolitics, cyber events, and market economics are increasingly one system. A ransomware incident at a logistics provider becomes a missed quarter for the retailer that depends on it. A sanctions action reroutes shipping and moves a commodity. A critical vulnerability in a widely deployed product becomes a disclosure event for every issuer that runs it. Organizations that see these connections early act before their competitors do; most see them late, because the evidence sits in different teams, tools, and feeds. Y2 treats open-source information as a first-class intelligence source and organizes it with an ontology: a semantic layer that says what each record is about and how the things it mentions relate. Cyber-market fusion is where that layer pays off for one question: **what is happening to this company and to the parties it depends on?** ## What the fused view does For an organization or vendor, fusion: 1. **Builds the exposure path.** Suppliers, customers, parents, subsidiaries, and operated systems come from the shared catalog's relations and from your workspace's evidenced ledger ties. A ledger tie carries its source and verification state, so you can see why a party is on the path. 2. **Gathers events about everyone on the path.** CVEs that affect them, actors that target them, Y2 signals by domain, incidents, prediction markets, and your workspace's financial facts. 3. **Places them on one timeline**, labeled by domain, severity, path, and provenance, with every event linked to its source. 4. **Pairs cyber events with the market events that followed them** on the timeline, within a window you choose. 5. **Shows which of your topics sit on the path**, so a supplier's breach surfaces on the topic that tracks the company that depends on it. ## What it deliberately does not claim An adjacency says that a market event followed a cyber event within the window, and whether both reached the company along the same path. It does not say that one caused the other. Markets move for many reasons, and a timeline cannot separate them. Fusion shows the timing so an analyst can investigate; it does not produce a causal score, and it does not hide the uncertainty in a single number. The view is also honest about coverage. Events without a source time stay in an **Undated** list instead of appearing at a guessed date. Every source is read within a budget, and the response says which budgets were reached and which sources come from a bounded recent scan. An empty timeline means no events were found within those limits; it does not mean the company has no exposure. ## Why the ledger matters here A fused view is only as good as its exposure path. Catalog relations are broad but come without a retrieval, an excerpt, or a verification state. Ledger ties are narrower and carry all three: the supplier is on the path because a named public page says so, graded `located` or `cross-linked`. Recording the ties that matter to your analysis, with evidence, is what makes the fused view yours and defensible. ## Related - [Tutorial: map a company's exposure](/docs/ontology/tutorial) - [Fusion response](/docs/ontology/reference/fusion) - [Why a claim ledger](/docs/ontology/explanation/claim-ledger) --- # Correct, Supersede, or Retract a Claim (/docs/ontology/how-to/correct-a-claim) The ledger never edits a line. Every change is a new line for the same claim key (`predicate|from|to|qualifier`) that supersedes the key's current head. The earlier lines stay in the history, so you can always see what was believed before. ## Find the current head In the app, the ledger panel shows the live claim for each designation and tie. Through the API, fold the subject and read `claimId` from the claim: ```bash curl https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567 \ --header "Authorization: Bearer $Y2_API_KEY" ``` ## Strengthen a claim with a second source In the app, record the same identifier again with the second page and a higher state such as `cross-linked`. The new line supersedes the head and keeps citing the head's evidence, so both pages support it. Through the API, send `supersedes` with the head's `claimId` and cite both observations: ```bash curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --json '{"kind":"claim","claim":{"predicate":"designated-by","from":"sbj_…","to":"dsg_…","supersedes":"clm_…head","confidence":90,"verification":"cross-linked","evidenceRefs":["obv_…first","obv_…second"]}}' ``` ## Close an interval when a page changes When a later retrieval no longer shows an identifier, supersede the claim with an end bound instead of retracting it. The identifier held for the interval you observed. ```json { "kind": "claim", "claim": { "predicate": "designated-by", "from": "sbj_…", "to": "dsg_…", "supersedes": "clm_…head", "confidence": 75, "verification": "self-asserted", "validFrom": "2026-03-01T09:55:00Z", "validTo": "2026-04-02T09:00:00Z", "evidenceRefs": ["obv_…first-retrieval", "obv_…later-retrieval"], "note": "Sighting interval between two retrievals; the page may have listed it earlier." } } ``` The end is exclusive. Leave a bound empty when no source gives it: an empty bound means unknown, never a guessed date. ## Retract a mistaken claim A retraction clears confidence, verification, and the interval, and still cites the observation that explains why, usually an analyst note: ```json { "kind": "claim", "claim": { "op": "retract", "predicate": "designated-by", "from": "sbj_…", "to": "dsg_…", "supersedes": "clm_…head", "evidenceRefs": ["obv_…note"], "note": "Attached on a shared name token only." } } ``` The retracted head leaves the present picture and stays in history. You can assert the same key again later by superseding the retraction. ## If the request is rejected | Response | Cause | Fix | | --- | --- | --- | | `409` with `assertion.chain` | `supersedes` is missing, or points at a line that is no longer the head | Fold the subject again and supersede the current head | | `400` with `assertion.verification` | The state is stronger than the cited evidence | Cite a second page, or lower the state | | `400` with `assertion.retract` | A retraction carries confidence, verification, or an interval | Remove them | See [Ledger record format](/docs/ontology/reference/ledger-format#rules) for every rule code. --- # Export STIX and Verify the Seal (/docs/ontology/how-to/export-and-verify) ## Export a subject as STIX 2.1 In the app, choose **Export STIX 2.1** in the ledger panel. It downloads a bundle for the subject at the **Valid on** date you set. Through the API, with `ledger:read`: ```bash curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567/stix" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "history=true" > northwind.stix.json ``` `history=true` adds retracted claims as relationships with `revoked: true`. Without it, the bundle holds only live claims. See [STIX 2.1 export](/docs/ontology/reference/stix-export) for the mapping. An incomplete subject neighborhood returns `400 VALIDATION_ERROR` instead of a partial bundle. Export the complete sealed log below, then run `bun packages/ontology/src/cli.ts stix ledger.ndjson` to project it offline. ## Export the sealed log Page through the log with `afterSeq`. The log is append-only, so continuing from `meta.nextAfterSeq` never skips or repeats a line: ```bash after=0 : > ledger.ndjson while :; do page=$(curl --silent --get "https://api.y2.dev/api/v2/ledger/records" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "afterSeq=$after" --data-urlencode "limit=200") echo "$page" | jq -r '.data[].line' >> ledger.ndjson after=$(echo "$page" | jq '.meta.nextAfterSeq') [ "$(echo "$page" | jq '.meta.hasMore')" = "true" ] || break done ``` Each `line` is the exact canonical text that was sealed. Write it byte for byte; do not reformat it. ## Check the export offline The reference checker ships in the portable `@y2/ontology` package. From the root of the Y2 repository: ```bash bun packages/ontology/src/cli.ts check ledger.ndjson ``` It verifies canonical JSON, each line's SHA-256, the `prev` chain, strictly increasing record time, and every rule in the [record format](/docs/ontology/reference/ledger-format). It prints `ok` or one line per problem with its rule code. `fold` and `stix` subcommands read the same file with `--recorded-at` and `--valid-at` cursors. ## Verify the seal in place To check the stored log without exporting it, verify windows of the seal: ```bash curl --get "https://api.y2.dev/api/v2/ledger/verify" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "afterSeq=0" --data-urlencode "limit=200" ``` The response reports `ok`, the number of lines checked, and any problems by `seq`. Continue from `meta.nextAfterSeq` until `hasMore` is false. Seal verification recomputes hashes and links; rules that look up earlier records are enforced at append time and by the offline checker. --- # Read the Ledger at a Point in Time (/docs/ontology/how-to/read-at-a-point-in-time) Two cursors control a fold. Set either, both, or neither: | Cursor | Parameter | Question it answers | | --- | --- | --- | | Record time | `recordedAt` | What had the ledger recorded by then? | | Valid time | `validAt` | Which claims hold in the world at that time? | ## See what holds on a date In the app, set **Valid on** in the ledger panel. Designations whose interval excludes that date move to **Outside the valid interval**; they are not deleted. Through the API: ```bash curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "validAt=2026-05-01T00:00:00Z" ``` ## See what you believed before a correction Set `recordedAt` to a time before the correction line was recorded. The fold uses only lines recorded by then, so a claim you later retracted is still the head: ```bash curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "recordedAt=2026-03-05T12:00:05Z" ``` ## Combine both "What did we believe on March 20 about April 10?" sets both: ```bash curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "recordedAt=2026-03-20T00:00:00Z" \ --data-urlencode "validAt=2026-04-10T00:00:00Z" ``` Record time selects a prefix of the log; valid time then filters the heads of that prefix. Valid time never walks back to an older version of a claim. ## Export the same view The STIX export takes the same cursors, so a partner receives exactly the picture you read: ```bash curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567/stix" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "recordedAt=2026-03-20T00:00:00Z" \ --data-urlencode "validAt=2026-04-10T00:00:00Z" ``` Times accept any ISO 8601 instant; the ledger normalizes them to UTC milliseconds. --- # Record a Claim with Evidence (/docs/ontology/how-to/record-evidence) Use this when a public page tells you something about a company, person, or system: a domain it uses, its SEC CIK, the host that serves its portal, or a supplier it depends on. You need an owner, admin, or member role in the workspace. In the API, use a workspace-bound key with `ledger:write`. ## Record an identifier in the app 1. Open the company from **FININT → Markets → Companies**. If **Entity ledger** has no subject, choose **Open ledger subject**. 2. In **Record an identifier with its source**, choose the kind and enter the value as the page shows it. Y2 stores the canonical form: lowercase email and hosts, `AS` plus the number for an ASN, a CIK padded to ten digits, and an uppercase ticker. 3. Set the namespace when the kind needs one: | Kind | Namespace | | --- | --- | | `handle` | The platform or host, such as `github.com` | | `external-id` | `sec-cik`, `lei`, `wikidata`, or `attack-group` | | `ticker` | The exchange's ISO 10383 MIC, such as `XNAS` | | `public-key` | The algorithm, such as `ssh-ed25519` | 4. Enter the public source URL, the method, and a short excerpt (the quote that shows the identifier). The excerpt is capped at 500 characters; the ledger keeps a quote and its SHA-256, never a page body. 5. Choose the verification state the evidence supports, and a confidence from 0 to 100. 6. Choose **Record claim**. ## Record a supply or ownership tie in the app In **Record a supply or ownership tie**, search the shared catalog for the other company, pick its role (supplier, customer, parent, or subsidiary), cite the page that states the tie, and choose **Record tie**. Y2 opens a ledger subject for the other company if your workspace has none and anchors it to the catalog entity. ## Record through the API Append the observation first, then the designator, then the claim that cites the observation: ```bash curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Idempotency-Key: dns-portal-2026-06-01" \ --json '{"kind":"observation","observation":{"method":"public-dns","sourceGrade":"primary","sourceUrl":"https://dns.example/lookup?name=portal.northwind.example","retrievedAt":"2026-06-01T11:50:00Z","excerpt":"portal.northwind.example A 192.0.2.10 (AS64500)","collector":"dns-sync"}}' ``` A designator with the same kind, namespace, and normalized value is reused: the response is `200` with `created: false` and the existing `dsg_…` ID. A claim that repeats a claim key must supersede the current head (see [Correct, supersede, or retract a claim](/docs/ontology/how-to/correct-a-claim)). ## Choose the verification state Pick the lowest state that is true. The ledger rejects states the cited evidence cannot support. | If the evidence is | Use | | --- | --- | | One public page that ties the identifier to the subject | `located` | | The subject's own page stating it | `self-asserted` | | Two retrieved pages at different URLs that each identify it | `cross-linked` | | A further independent source that agrees | `corroborated` | | Only an analyst note connecting other observations | `analytic` | | A page where the string appears without a tie | `mentioned` | `cross-linked` and `corroborated` need at least two retrievals of different pages among the cited observations. `analytic` means every cited observation is an analyst note. A public profile shows that a handle exists and what it displays. It does not show who can sign in to it. Record `designated-by` for the handle, and record `controls` only when a public source speaks to control, usually at low confidence. ## Related - [Vocabulary](/docs/ontology/reference/vocabulary) for every kind, method, and predicate. - [Ledger record format](/docs/ontology/reference/ledger-format) for the rules and their error codes. --- # Ontology and Cyber-Market Fusion (/docs/ontology) Most enterprise data loses its meaning at department boundaries: the security team tracks a vendor's hosts, the finance team tracks its filings, and nobody connects a breach at a supplier to a guidance cut two days later. Y2 keeps those threads together in two layers: - **The entity ledger** is your workspace's append-only record of what public sources say about a company, person, or system: its names, domains, ASNs, handles, SEC CIK, ticker, suppliers, and the systems it operates. Every claim cites a public observation and carries a verification state and a time interval. Nothing is edited in place and nothing is merged on a shared name. - **Cyber-market fusion** reads the ledger together with Y2's shared catalog to place cyber, market, supply-chain, and geopolitical events about a company, and about the parties it depends on, on one timeline. It shows which of your topics sit on that exposure path. ```mermaid flowchart LR subgraph workspace [Your workspace] L[Entity ledger: evidenced claims] F[Financial history] T[Topics] end subgraph catalog [Y2 shared catalog] E[Entities and relations] C[CVEs and threat actors] S[Signals, incidents, markets] end L -- anchored to --> E E --> X[Cyber-market fusion] L --> X C --> X S --> X F --> X X --> T ``` ## Find what you need This section follows the [Diátaxis](https://diataxis.fr/) structure: tutorials to learn, how-to guides to get a task done, reference to look things up, and explanation to understand the design. Anchor a ledger subject, record evidence, and read the fused timeline, start to finish ### How-to guides Add identifiers, systems, and supply ties that cite a public page Change what the ledger believes without rewriting history Ask what was recorded by one date about the world on another Share claims as STIX 2.1 and prove the log was not edited ### Reference Records, fields, the seal, IDs, and every rule a line must satisfy Subject classes, designator kinds, methods, predicates, and the verification ladder How ledger records map to STIX objects and the Y2 extension Domains, exposure paths, provenance, adjacencies, and coverage HTTP endpoints, scopes, and the generated operation reference ### Explanation Append-only claims, three clocks, and identity that never merges on a shared string The thesis, what fusion shows, and what it deliberately does not claim How the shared catalog and your workspace ledger fit together --- # Fusion Response (/docs/ontology/reference/fusion) `GET /api/v2/entities/{entityId}/fusion` and the in-app **Cyber-market exposure** panel return the same view for an organization or vendor entity. ## Parameters | Parameter | Default | Range | Meaning | | --- | --- | --- | --- | | `windowDays` | 90 | 1–365 | Days of history ending at request time | | `adjacencyDays` | 7 | 1–90 | How long after a cyber event a market event counts as adjacent | ## Response fields | Field | Meaning | | --- | --- | | `entity` | The resolved company (after catalog merge redirects) | | `window` | `start`, `end`, and `adjacencyDays` actually used | | `counterparties` | Up to 12 parties on the exposure path, with `relation`, `provenance`, and, for ledger ties, `confidence` and `verification` | | `identifiers` | Live designations of ledger subjects anchored to the company | | `topics` | Workspace topics whose subject is the company or a counterparty, with the path relation | | `timeline` | Up to 200 dated events in the window, newest first | | `undated` | Events without a source time, never placed at a guessed date | | `summaries` | Per domain: event count, highest severity, latest event time | | `adjacencies` | Up to 50 cyber → market pairs within `adjacencyDays`, same-path pairs first | | `coverage` | `truncated` and plain-language notes on read budgets and source limits | ## Exposure paths | Relation | From a catalog relation | From a ledger claim | | --- | --- | --- | | `supplier` | The party `supplies` the company | The party's subject `supplies` the company's subject | | `customer` | The company `supplies` the party | The company's subject `supplies` the party's subject | | `parent` | The company is `subsidiary_of` the party | The company's subject is `subsidiary-of` the party's subject | | `subsidiary` | The party is `subsidiary_of` the company | The party's subject is `subsidiary-of` the company's subject | | `operated-system` | The company `operates` or `uses` a software, API service, or model entity | The company's subject `operates` a system subject | A ledger tie wins over a catalog tie to the same entity because it carries evidence and a verification state. Catalog exposure paths use the resolved relation projection. Changing `windowDays` filters event times; it does not reconstruct historical catalog relations or what Y2 knew at an earlier date. Ledger exposure paths use claims valid at `window.end`. ## Event sources | Domain | Sources | | --- | --- | | `cyber` | CVEs that `affect`, and actors that `target`, the company or a path party; readable cyber signals; cyber incidents | | `market` | Readable market signals; the workspace's financial facts at their information date; prediction markets about a path party; economic incidents | | `supply_chain` | Ledger supply and ownership ties at their `valid_from`; readable supply-chain signals; maritime and aviation incidents | | `geopolitical` | Readable geopolitical, military, and policy signals; conflict and political incidents | Signals are readable when they are global or community, belong to the key's workspace, or are the caller's own private signals. ## Provenance | Value | Meaning | | --- | --- | | `catalog` | Y2's shared catalog: entities, relations, signals, incidents, markets | | `ledger` | Your workspace's entity ledger | | `workspace` | Your workspace's research, such as company financial history | ## Read budgets Fusion never scans without a bound. Catalog relations read up to 150 in each direction; each path party reads its 10 most recently added `affects` and 10 `targets` relations, selecting CVEs, threat actors, and malware for cyber events, and up to 15 signal subjects in the selected window. Financial history contributes the 12 most recently recorded facts; up to 5 anchored ledger subjects are folded and 200 workspace topics are read. Incidents and prediction markets still store entity links as arrays, so they come from the 100 most recently updated rows. `coverage.notes` states these limits in each response, and `coverage.truncated` is true when any source or output budget omits rows. A truncated response can therefore contain fewer than 200 events. Sources naming both the company and a counterparty retain the direct exposure path. --- # Ledger Record Format (/docs/ontology/reference/ledger-format) A ledger is a sequence of lines. Each line is one record, serialized as canonical JSON: object keys sorted by UTF-16 code unit, no insignificant whitespace, and integers only. Every field is present; `null` marks an unknown time or number and `""` an empty string field. The portable implementation is the `@y2/ontology` package. ## Common fields | Field | Type | Meaning | | --- | --- | --- | | `v` | `0` | Format version | | `seq` | integer | Position in the log, starting at 1, contiguous | | `id` | string | Record ID; the prefix names the kind | | `kind` | `subject`, `designator`, `observation`, `assertion` | Record kind | | `recorded_at` | time | Record time; strictly increasing along the log | | `prev` | hex or `null` | `hash` of the previous line; `null` on the first | | `hash` | hex | SHA-256 of the canonical line without `hash` | Times are ISO 8601 in UTC with milliseconds, exactly as `Date.prototype.toISOString` prints them. ## Records ### Subject | Field | Meaning | | --- | --- | | `class` | `person`, `organization`, or `system`. A subject never changes class. | A subject has no name. Names and identifiers arrive as designators and claims. ### Designator | Field | Meaning | | --- | --- | | `designator_kind` | See [Vocabulary](/docs/ontology/reference/vocabulary#designator-kinds) | | `value` | The canonical string | | `namespace` | Platform, algorithm, registry, or exchange MIC; `""` for kinds without one | | `normalized` | Case-folded for names and handles; otherwise equal to `value` | One designator per `(kind, namespace, normalized)` in a ledger. A second line with the same key is rejected; reuse the existing ID. ### Observation | Field | Meaning | | --- | --- | | `method` | One passive method, or `analyst-note` | | `source_grade` | `primary`, `secondary`, or `analytic` (only for a note) | | `source_url` | The page; required for a retrieval, optional for a note | | `archive_url` | A preserved copy, or `null` | | `retrieved_at` | Retrieval time; `null` on a note | | `content_hash` | SHA-256 of `excerpt`, or of an archived blob when `archive_url` is set | | `excerpt` | A quote of 1–500 characters, or `null` when an archive holds the content | | `collector` | A local label for the recorder, 1–80 characters, never a personal name in Y2 | | `cites` | Observations a note connects; empty on a retrieval | ### Assertion | Field | Meaning | | --- | --- | | `op` | `assert` or `retract` | | `predicate`, `from`, `to` | The claim; see [predicates](/docs/ontology/reference/vocabulary#predicates) | | `qualifier` | The public role on `member-of`; `""` otherwise | | `supersedes` | The current head of the claim key, or `null` on the key's first line | | `confidence` | Integer 0–100; `null` on a retraction | | `verification` | A [ladder state](/docs/ontology/reference/vocabulary#verification-ladder); `null` on a retraction | | `valid_from`, `valid_to` | Valid-time interval, end exclusive; `null` is unknown, never guessed | | `evidence_refs` | 1–20 earlier observations | | `note` | Up to 1,000 characters of caveat | The claim key is `predicate|from|to|qualifier`. Each key has exactly one chain of lines. ## Identifiers Y2 derives record IDs with its public-ID strategy: a prefix and the first 24 hex characters of SHA-256 over the record type and the workspace ledger position. | Kind | Prefix | Example | | --- | --- | --- | | Subject | `sbj_` | `sbj_0123456789abcdef01234567` | | Designator | `dsg_` | `dsg_0123456789abcdef01234567` | | Observation | `obv_` | `obv_0123456789abcdef01234567` | | Assertion | `clm_` | `clm_0123456789abcdef01234567` | The offline format accepts any lowercase ID with the right prefix, such as `sbj_ada` in the fictional fixtures. ## Rules Each rule has a code. The API returns the codes in `detail`; the offline checker prints them per line. | Code | Rule | | --- | --- | | `shape.*` | Exact field set and field types for the kind | | `format.json`, `format.canonical` | The line is JSON in canonical form | | `chain.seq`, `chain.prev`, `chain.hash` | Contiguous `seq`, `prev` links the previous hash, `hash` matches | | `chain.time`, `time.format` | Record time strictly increases; times use the exact format | | `id.format`, `id.duplicate` | Prefix matches the kind; IDs are unique | | `designator.*` | Canonical value for the kind and namespace; computed `normalized`; one row per key | | `observation.method`, `observation.grade` | Passive method; only a note is `analytic` | | `observation.url`, `observation.time` | Exact http(s) URL without userinfo; a retrieval has its URL and time, not after record time | | `observation.note`, `observation.cites` | A note cites earlier observations and has no retrieval time; a retrieval cites nothing | | `observation.excerpt`, `observation.hash`, `observation.preservation` | Excerpt 1–500 characters; hash matches the excerpt unless archived; keep an excerpt or an archive | | `assertion.endpoint` | `from` and `to` match the predicate; distinct ends; same class for `same-as` and `distinct-from` | | `assertion.qualifier` | Only `member-of` carries a lowercase role | | `assertion.chain` | First line supersedes nothing; later lines supersede the current head; no double retraction | | `assertion.evidence` | 1–20 distinct earlier observations | | `assertion.confidence`, `assertion.interval` | Integer 0–100; `valid_from` before `valid_to` | | `assertion.verification` | The state is one the evidence supports (see the ladder) | | `assertion.retract` | A retraction clears confidence, verification, and the interval | | `assertion.label` | `preferred-label` needs a live `designated-by` for the same pair | ## Example The first two lines of the fictional fixture, wrapped here for reading: ```json {"class":"person","hash":"…","id":"sbj_ada","kind":"subject","prev":null, "recorded_at":"2026-03-01T10:00:00.000Z","seq":1,"v":0} {"designator_kind":"published-name","hash":"…","id":"dsg_ada_name","kind":"designator", "namespace":"","normalized":"ada example","prev":"…","recorded_at":"2026-03-01T10:00:01.000Z", "seq":2,"v":0,"value":"Ada Example"} ``` The complete fixtures, `fictional.ndjson` and `fictional-cyber-market.ndjson`, are in `packages/ontology/v0/examples` in the Y2 repository. --- # STIX 2.1 Export (/docs/ontology/reference/stix-export) A STIX export projects one subject's fold at the requested cursors into an OASIS STIX 2.1 bundle. It is a projection: the ledger remains the store, and re-exporting the same fold yields the same object IDs. The subject neighborhood is bounded. If it exceeds the read budget, the endpoint rejects the export; export the paginated ledger records and project the complete log offline instead. ## Mapping | Ledger | STIX 2.1 | | --- | --- | | `person` subject | `identity`, `identity_class: individual` | | `organization` subject | `identity`, `identity_class: organization` | | `system` subject | `identity`, `identity_class: system` | | Live preferred label | `identity.name`. An unlabeled subject is named by its ledger ID. | | Other published names and pseudonyms | `designated_names` in the extension; no `threat-actor` is created to hold aliases | | `external-id` designations | `identity.external_references` (`sec-edgar-cik`, `lei`, `wikidata`, `mitre-attack`) | | `ticker` designations | `identity.external_references` with `source_name: ticker` and `MIC:SYMBOL` | | `email` | `email-addr` | | `handle` | `user-account` with `account_login` and `account_type` from the namespace; no credential or login history | | `domain`, `hostname` | `domain-name` | | `url` | `url` | | `asn` | `autonomous-system` | | `designated-by` to an observable | `relationship` of type `designated-by` | | Other claims | `relationship` with the predicate as its type, `confidence`, `start_time`, and `stop_time` | | Unconfirmed `same-as` | `relationship` of type `related-to`, described as a hypothesis | | `follows` | `relationship` of type `follows` between two `user-account` objects | | Cited observations | `external_references` on the relationship: method as `source_name`, URL, excerpt as `description`, `hashes.SHA-256` | | Retracted claim | Omitted; with `history=true`, a relationship with `revoked: true` | `duplicate-of` and `attributed-to` are never emitted: identity is not confirmed by the ledger. ## Extension Every exported identity and relationship carries a property extension defined in the bundle: ```json { "type": "extension-definition", "spec_version": "2.1", "name": "Y2 passive entity ledger", "schema": "https://y2.dev/docs/ontology/reference/stix-export", "version": "0.1.0", "extension_types": ["property-extension"] } ``` | Property | On | Meaning | | --- | --- | --- | | `ledger_subject_id` | identity | The `sbj_…` ID | | `designated_names` | identity | Names other than the label | | `ledger_claim_id` | relationship | The head line's `clm_…` ID | | `claim_key` | relationship | `predicate|from|to|qualifier` | | `predicate` | relationship | The ledger predicate, even when the type is `related-to` | | `verification` | relationship | The [ladder state](/docs/ontology/reference/vocabulary#verification-ladder) | | `qualifier`, `note` | relationship | When present | A `Y2` producer `identity` is the `created_by_ref` of the extension, identities, and relationships. ## Identifiers and timestamps STIX requires `type--UUID` identifiers. The UUID carries a SHA-256 digest, the same derivation Y2 uses for public IDs, laid out as an RFC 9562 name-based UUID (version 8): | Object | Digest input | | --- | --- | | Cyber observables | The type and the STIX ID-contributing properties, so equal observables share an ID across exports | | Identities | The type, a one-way per-workspace scope, and the ledger subject ID | | Relationships | The type, the scope, and the claim key | The workspace ID is never exported. A relationship's `created` is the first line of its claim key; `modified` is the head line's record time. An identity's `modified` is the later of its opening and the record times of its designation and label claims, including retractions. STIX revocation is permanent. Reasserting a retracted claim starts a new relationship ID and a new `created` time; later corrections keep that new ID. The extension retains the original claim key. An explicit `validAt` cursor adds that snapshot time to the identity and relationship ID scope. STIX has one version clock, so different valid-time pictures cannot share an object version with different contents. The ledger subject ID in the extension still joins those snapshots. Bundle IDs digest the full exported content; repeating an unchanged projection yields the same bundle ID. --- # Ledger Vocabulary (/docs/ontology/reference/vocabulary) The vocabulary is closed: a value outside these lists is rejected. Terms marked **Y2 profile** extend the base framework for issuer and supply-chain research and follow the same evidence, verification, and interval rules. ## Subject classes | Class | Examples | | --- | --- | | `person` | A researcher, an executive, a maintainer | | `organization` | A company, a vendor, an agency, a threat group identified by its ATT&CK ID | | `system` | A site, a host, a repository, a service, a model, an autonomous system | ## Designator kinds | Kind | Value | Namespace | | --- | --- | --- | | `published-name` | The name as the page prints it | — | | `pseudonym` | A name sources present as an alias | — | | `handle` | The login, without `@` | Platform or host, such as `github.com` | | `email` | Lowercase addr-spec | — | | `url` | Exact http(s) URL without userinfo | — | | `domain`, `hostname` | Lowercase host, no trailing dot | — | | `asn` | `AS` and a decimal number | — | | `public-key` | Lowercase hex fingerprint | Algorithm, such as `ssh-ed25519` | | `external-id` | A registry identifier | `wikidata` (`Q…`), `lei` (20 characters with valid check digits), `attack-group` (`G` and four digits), `sec-cik` (ten digits, **Y2 profile**) | | `ticker` (**Y2 profile**) | Uppercase listed symbol | ISO 10383 exchange MIC, such as `XNAS` | ## Observation methods `public-web-page`, `public-profile`, `public-dns`, `certificate-transparency`, `public-archive`, `public-registry`, `public-code-host`, and `analyst-note`. Interaction with the subject, authentication, and non-public access have no method name, so they cannot be recorded as if they were valid sources. ## Predicates | Predicate | From | To | Meaning | | --- | --- | --- | --- | | `designated-by` | subject | designator | The subject is referred to by this identifier | | `preferred-label` | subject | name designator | Display this name; needs a live `designated-by` | | `same-as` | subject | subject of the same class | Hypothesis that the rows are one entity | | `distinct-from` | subject | subject of the same class | Hypothesis that they are not | | `member-of` | person | organization | A public role, in `qualifier` | | `affiliated-with` | subject | subject | A weaker public tie | | `controls` | person or organization | system or designator | Claimed control; stays weak unless a source speaks to control | | `operates` | person or organization | system | A source says this party runs that system | | `resolves-to` | designator | designator | A public resolution for the interval observed | | `follows` | handle | handle | A public page shows one account following another | | `supplies` (**Y2 profile**) | organization | organization | The first supplies goods or services to the second | | `subsidiary-of` (**Y2 profile**) | organization | organization | The first is owned or controlled by the second | `same-as` and `distinct-from` never merge rows. When both are live for a pair, the pair is contested. ## Verification ladder Verification belongs to a claim, not to a subject. | State | Honest use | Evidence the ledger requires | | --- | --- | --- | | `unverified` | Written down, not checked | At least one observation | | `mentioned` | The string appeared; no page ties it | A public retrieval | | `located` | One retrieved page ties them | A public retrieval | | `self-asserted` | The subject's own page states it | A public retrieval | | `cross-linked` | Two independent pages identify the link | Retrievals of two different pages | | `corroborated` | A further independent source agrees | Retrievals of two different pages | | `contradicted` | A source disputes the link | A public retrieval | | `stale` | The page is gone or no longer shows it | A public retrieval | | `analytic` | The only support is an analyst note | Only analyst notes | A claim supported only by notes is `analytic` or `unverified`. ## Source grades | Grade | Meaning | | --- | --- | | `primary` | The page is the thing itself: the profile, the staff page, the registry entry | | `secondary` | The page reports on the thing | | `analytic` | An analyst note connecting other observations; it must cite them | --- # Tutorial: Map a Company's Cyber-Market Exposure (/docs/ontology/tutorial) In this tutorial you will record what a public page says about a company, link one of its suppliers, and read the cyber and market events that reach the company through that supplier. It takes about ten minutes. You need a workspace with FININT, Cyber, or Explorer access and an owner, admin, or member role. We will use a company from the shared catalog. Any organization or vendor works; the examples use Northwind Example Holdings and its supplier Contoso Example Logistics. ### Open the company Go to **FININT → Markets → Companies** and select the company. The company page shows its shared identity, your workspace's financial history, the **Cyber-market exposure** panel, and the **Entity ledger** panel. ### Open a ledger subject In **Entity ledger**, choose **Open ledger subject**. Y2 appends a subject line of class `organization` and anchors it to the catalog company. The subject has no name yet: opening a row is not a finding. Names and identifiers arrive as claims with evidence. ### Record the company's domain with its source In **Record an identifier with its source**, enter: | Field | Value | | --- | --- | | Kind | Domain | | Value | `northwind.example` | | Public source URL | The company's home page | | Method | Public web page | | Excerpt | The sentence on the page that shows the domain | | Verification | Self-asserted (the company's own page says so) | | Confidence | 85 | Choose **Record claim**. Y2 records three lines in one transaction: the `domain` designator, the observation (source URL, retrieval time, excerpt, and its SHA-256), and a `designated-by` claim that cites the observation. The domain now appears in the ledger with its verification badge. ### Add a published name and make it the label Record a second identifier of kind **Published name** from the same page. It appears as a designation, but the subject still has no display label: a label is its own claim, never a guess. Choose **Use as label** next to the name. Y2 records an analyst note that cites the page you used, and a `preferred-label` claim graded `analytic` because its only support is that note. ### Link the supplier In **Record a supply or ownership tie**, search the shared catalog for the supplier, choose the role **Supplier of this company**, and cite the page that names it, such as the company's annual filing on a public registry. Y2 opens a ledger subject for the supplier if your workspace has none, then records a `supplies` claim from the supplier to the company. ### Read the exposure timeline Return to the company. **Cyber-market exposure** now lists the supplier on the exposure path with `ledger · located`, next to any suppliers the shared catalog already knows. CVEs that affect the supplier, cyber signals about it, and market events about the company appear on one timeline. When a market event followed a cyber event within seven days, it is listed under **Market events after cyber events**. That list is timing on a shared path, not a claim of causation. ### Do the same through the API With a workspace-bound key that holds `ledger:read` and `ledger:write`: ```bash # Open the subject, anchored to the catalog entity. curl https://api.y2.dev/api/v2/ledger/subjects \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Idempotency-Key: open-northwind-1" \ --json '{"class":"organization","entityId":"ent_0123456789abcdef01234567"}' # Record the observation, then the designator, then the claim that cites it. curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --json '{"kind":"observation","observation":{"method":"public-web-page","sourceGrade":"primary","sourceUrl":"https://northwind.example/","retrievedAt":"2026-06-01T11:45:00Z","excerpt":"Northwind Example Holdings — northwind.example","collector":"integration"}}' curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --json '{"kind":"designator","designator":{"kind":"published-name","value":"Northwind Example Holdings"}}' curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --json '{"kind":"claim","claim":{"predicate":"designated-by","from":"sbj_…","to":"dsg_…","confidence":80,"verification":"self-asserted","evidenceRefs":["obv_…"]}}' ``` Then record the label as its own claim, which needs an analyst note that cites the page: ```bash curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --json '{"kind":"observation","observation":{"method":"analyst-note","sourceGrade":"analytic","excerpt":"Display the name the site prints.","collector":"integration","cites":["obv_…"]}}' curl https://api.y2.dev/api/v2/ledger/records \ --header "Authorization: Bearer $Y2_API_KEY" \ --json '{"kind":"claim","claim":{"predicate":"preferred-label","from":"sbj_…","to":"dsg_…","confidence":60,"verification":"analytic","evidenceRefs":["obv_…note"]}}' ``` Finally, read the fused view with a key that holds `intel:explorer`: ```bash curl --get "https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/fusion" \ --header "Authorization: Bearer $Y2_API_KEY" \ --data-urlencode "windowDays=90" ``` ## What you built You now have a small, sealed record of what public pages say about a company and one of its suppliers, with each claim graded by its evidence. Fusion uses those ties to bring the supplier's cyber exposure onto the company's timeline, and every event on it links back to its source. Next steps: - [Correct, supersede, or retract a claim](/docs/ontology/how-to/correct-a-claim) when a page changes. - [Read the ledger at a point in time](/docs/ontology/how-to/read-at-a-point-in-time) to see what you believed before a correction. - [Export STIX and verify the seal](/docs/ontology/how-to/export-and-verify) to share the claims. --- # Country Intelligence Cards (/docs/osint/country-cards) Country Intelligence Cards combine country-attributed observations, country instability data, news, prediction markets, market indices, and curated infrastructure. They are available with the Situation Room on Lite, Pro, and Elite plans. ## Open a country card ### Open the Situation Room Go to **OSINT → Monitor** and wait for the map to load. ### Explore the map Enable the event layers and time range you want to explore. The country card reads its own country-level data, independently of the map's loaded markers and enabled layers. ### Select a country Click or tap a country boundary. Y2 opens its card as a modal over the map. ### Close or export Select **Close**, click the backdrop, or press `Escape` to close the card. Select the download icon to save the currently assembled dossier as a UTF-8 text file. Curated country profiles provide fallback scores and a centroid for nearby infrastructure. Other recognized countries can still display country-attributed observations and available feeds. A missing infrastructure profile does not imply that news or country data is absent. ## How the card assembles data ```mermaid flowchart LR boundary["Selected country boundary"] baseline["Curated profile
Fallback scores · centroid"] cii["Country CII
Value · components"] code["ISO alpha-2 code"] events["Recent country observations
Signals · timeline · brief context"] feeds["Country feeds
News · prediction markets · stock"] refs["Curated registries
Bases · nuclear · ports"] card["Country card"] boundary --> code code --> events --> card code --> cii baseline --> card cii --> card code --> feeds --> card refs --> card ``` Selecting a country resolves its ISO alpha-2 code, such as `SA` for Saudi Arabia. Active Signals, the timeline, and brief context use a bounded query of up to **100 recent, unexpired observations** attributed to that country. Top News uses a separate query of eligible news and report sources. Panning, changing map layers, or narrowing the map time range does not filter these country queries. The card is a recent country dossier, not an exhaustive national event archive. ## Read the instability section The ring uses the country's current Conflict Indicators Index (CII) when available. The component breakdown shows country-level values where available and uses curated baselines as fallbacks: | Component | What the card labels | | --- | --- | | Civil unrest | Political and social instability | | Conflict | Conflict activity from the country event count | | Security | Security risk | | Information | Information environment | If country CII is unavailable, the ring uses an equal-weight average of the four fallback components. Countries without either country CII or a curated baseline have no measured score; the displayed fallback is zero. | Ring value | Ring color | | --- | --- | | 0–34 | Green | | 35–64 | Amber | | 65–100 | Red | With country CII, the badge uses that value: below 35 is `NORMAL`, 35–64 is `ELEVATED`, and 65 or above is `CRITICAL`. Without country CII, the badge uses the curated default status, or `NORMAL` when none exists. A fallback score or status is not a finding of no risk. ## Interpret signals and the timeline **Active Signals** counts the observations returned by the country query and groups them by event category. It does not add a separate severity breakdown. The **7-Day Timeline** limits those observations to events from the last seven days and maps these categories into four lanes: | Lane | Included event categories | | --- | --- | | Protest | `political` | | Conflict | `conflict` | | Natural | `seismic`, `weather`, `fire` | | Military | `maritime`, `aviation` | Categories such as economic, health, and cyber can appear in Active Signals but not in a timeline lane. A busy country can have more than 100 recent observations, so the timeline can omit older events even when they fall within seven days. ## Read the intelligence brief When no unexpired brief exists for the country, the card schedules an AI-generated brief. The generation context contains: - country name and two-letter code; - the country CII value, when available; - the count of observations returned by the country query; - the first 10 titles from that same set. The model is instructed to produce five sections in 300–400 words: current situation, military and security posture, key risk factors, regional context, and outlook. It must acknowledge low activity instead of inventing threats. Briefs expire after two hours. Y2 records a context hash based on 10-point CII buckets and five-event count buckets, but the card reads the newest unexpired brief for the country. A changed map selection does not force a refresh while that brief remains valid. The brief is generated from a small, bounded context. Validate important conclusions against the linked source records before acting on them. ## Country feeds These widgets use the selected country's ISO alpha-2 code: ### Top News The card shows up to eight recent, unexpired articles or report observations from RSS, GDELT, ACLED, News Terminal, and Y2 report sources. It excludes seismic and weather categories and deduplicates articles for display. Eligibility is applied before the result limit, so satellite fire detections cannot crowd out available news. Select a headline to open its source URL. Country selection uses geographic attribution. A story about a Saudi pipeline can appear under Saudi Arabia; an event on Mayun Island belongs under Yemen. A Red Sea event that merely mentions Saudi Arabia or affects its shipping does not automatically become a Saudi event. Regional exposure and mentioned countries are separate context, not additional event locations. Opening a card reads stored news rather than starting a fresh search. Scheduled ingestion supplies new observations, and the open card periodically refreshes expiry eligibility. The [News Signal Feed](/docs/news) can contain related regional items that do not qualify for this country selection. If the query fails, **Try again** retries the read. **No recent news** means the query returned no eligible country news; it does not establish that no real-world event occurred. Check [source health](/docs/osint/sources) and broader regional feeds when coverage seems incomplete. For integrations, the [country-news API](/docs/api/osint#read-country-news) returns canonical observations and preserves their public IDs. Its observation count can differ from the card's deduplicated headline count. ### Prediction markets The card selects up to five unexpired Polymarket or Kalshi contracts whose derived country tags include the selected code. Results are sorted by trading volume and show probability, volume, optional liquidity and end date, and the provider link. A missing section means that no current matching contract was found. ### Stock index For 45 configured countries, the card requests a primary national index from Yahoo Finance when no valid cache row exists. It displays the index name and percentage change between the latest close and the close five trading observations earlier. Results expire after one hour. Unsupported codes or provider failures leave the chip hidden. ## Infrastructure exposure The card lists the **three nearest** records in each curated registry: - military bases; - nuclear facilities; - strategic ports and chokepoints. Distance is the great-circle distance from the country's configured centroid. The assets are not filtered by political boundary, affiliation, or active map layers. “Exposure” therefore means nearby reference context, not confirmed ownership or presence inside the selected country. ## Download the dossier The download icon creates a file named `y2-intel-{iso3}-{YYYY-MM-DD}.txt`. It includes the status, ring and component values, active-signal counts, and any stock, brief, news, market, and infrastructure data already available when you select download. The export is a point-in-time client-side text snapshot. The current card does not provide a separate PDF or print command. ## Keyboard shortcuts | Key | Action | | --- | --- | | `Escape` | Close the country card | | `L` | Toggle the map layer sidebar while focus is not in an input | ## Next steps Explore map layers, regional feeds, and individual observations Check source schedules, health, and retention See how observations, specialized caches, and reference overlays differ --- # Data Infrastructure (/docs/osint/data-infrastructure) Y2 does not force every map layer into one universal record. It routes data according to its shape, update rate, and useful lifetime. This distinction matters when you compare source health, map freshness, search results, and API responses. ## Architecture at a glance ```mermaid flowchart LR sources["Scheduled providers
News · hazards · cyber · markets · tracking"] adapters["Source adapters
Fetch · validate · normalize"] health["Source control
Circuit state · last success · errors"] observations["Observation store
Events with time, place, severity, and provenance"] typed["Specialized caches
Aircraft · vessels · GPS · FININT · markets"] ontology["Ontology
Sources · entities · places · incidents · relations"] references["Reference overlays
Bases · cables · satellites · CCTV"] products["Product views
Situation Room · country cards · search · API"] sources --> adapters adapters --> health adapters --> observations adapters --> typed observations <--> ontology typed --> products observations --> products ontology --> products references --> products ``` The diagram shows three storage paths: | Path | Best suited to | Examples | | --- | --- | --- | | Normalized observations | Time-bound events that can be searched, grouped, and linked | Earthquakes, fires, disaster alerts, threat indicators, news events, Y2-extracted signals | | Specialized caches | Data with a provider-specific shape or refresh cycle | Aircraft tracks, vessel positions, GPS interference, prediction markets, FININT indicators | | Reference overlays | Curated context rendered directly by the app | Military bases, nuclear facilities, undersea cables, satellite anchors, CCTV hubs | A reference overlay can appear on the map without creating a normalized observation or a source-health record. Its presence does not imply that a provider fetch just succeeded. ## From provider response to observation ### Check the source circuit Before a scheduled adapter calls its provider, it checks that source's circuit-breaker state. Five consecutive failures open the circuit for a five-minute cooldown. Other sources continue independently. ### Normalize the provider payload The adapter maps provider-specific fields to Y2 concepts such as source, title, category, severity, event time, coordinates, country, URL, fetch time, and expiry. The original provider payload may also be retained as source-specific metadata. ### Resolve ontology references Where the adapter has enough context, Y2 resolves or creates the related data source, place, entities, and incident. These links are optional: an observation can remain useful before all ontology references are available. ### Upsert by source identity Normalized observations use the pair `(sourceType, sourceId)` as their source identity. A later fetch updates the active observation instead of blindly appending another row. Ambiguous legacy ownership fails closed rather than overwriting an arbitrary record. For News Terminal RSS, a reused publisher identifier paired with a different article URL creates a separate observation. The original article, public identity, and evidence links remain intact, and the incoming observation retains its provider identity in provenance. ### Recompute dependent intelligence Writes linked to an incident enqueue corroboration work. Separate scheduled jobs classify observations, recompute conflict indicators, update statistical baselines, and reconcile stale incidents and markets. ## Observation and ontology model An observation is the evidence-bearing event record. It can carry: - provider identity and raw metadata; - event time, fetch time, and expiry; - category, severity, coordinates, region, and country; - provenance tier: `primary`, `derived`, or `synthesized`; - temporal meaning: `ongoing`, `past`, `forecast`, or `analysis`; - links to a canonical data source, place, entities, and incident. The ontology supplies durable identity around those observations: ```mermaid erDiagram DATA_SOURCE ||--o{ OBSERVATION : produces PLACE ||--o{ OBSERVATION : locates INCIDENT ||--o{ OBSERVATION : groups ENTITY }o--o{ OBSERVATION : appears_in ENTITY ||--o{ ENTITY_RELATION : connects OBSERVATION ||--o{ RELATION_EVIDENCE : supports ENTITY_RELATION ||--o{ RELATION_EVIDENCE : has ``` Entity aliases, provider identifiers, and canonical names have dedicated lookup indexes. Merge redirects preserve references when duplicate objects converge, while integrity jobs audit resolver indexes and candidate duplicates. Automated destructive entity merging is intentionally fail-closed until dependent references can be rewritten and verified. ### Identity keys and safe deduplication Y2 keeps durable object identity separate from the text used to find an object. A Convex document ID is the stable reference. Names, aliases, and provider identifiers are normalized into dedicated lookup projections: | Identity path | Indexed lookup | | --- | --- | | Canonical name | entity kind + normalized canonical key | | Alias | entity kind + normalized alias key | | Provider identifier | entity kind + provider + normalized provider value | | Ownership | object type + identity namespace + claim key | Normalization applies Unicode compatibility normalization, trims and collapses whitespace, and compares case-insensitively while retaining the original display text. Candidate aliases are deduplicated before resolution. These are typed compound indexes, not application-encoded byte keys. Convex owns physical ordering; Y2 chooses index fields in query order, uses indexed equality/range reads, and paginates large partitions. Reverse-time queries use numeric timestamps and descending index order instead of inverted timestamp strings. Opaque Convex IDs are never parsed for time or locality. An index row is a rebuildable projection, not permission to choose an identity winner. Y2 can automatically remove a dead, stale, kind-mismatched, or same-owner duplicate projection. If a normalized key has multiple live owners, the resolver keeps both objects, skips the unsafe alias transfer, and records one review case. A merge occurs only through the governed reference-rewrite and verification process. ### Country attribution and regional exposure A country-filtered observation view uses the observation's geographic attribution. Text-based resolution prioritizes headline locations and specific places, with matched text and resolution method retained as provenance. Countries mentioned in the story and countries exposed to a regional event are recorded separately when available. Those associations do not turn every regional event into a local event for each affected country. The News Terminal and country-news views reuse the existing observation, entity, place, and source model. A topic-feed item may not yet have an eligible, country-attributed observation. Compare [News feeds](/docs/news) with [country-card selection](/docs/osint/country-cards#top-news) before treating different result counts as missing data. For a worked explanation of shared identity and evidence across domains, read [how Y2 connects cyber, market, and global tracking](/blog/y2-ontology-cyber-market-global-tracking). ## Agent Y2 signals Completed Y2 reports can be processed into structured observations with extraction context, resolved entities, places, and incident links. Intel discovery and expansion jobs can create their own observations through the same ontology layer. These synthesized records support investigation graphs, evidence links, search, and other intelligence views. They are not a promise that every future report automatically uses all prior observations as research context. ## Freshness and retention Freshness is controlled per data path rather than by one platform-wide timer. | Data | Typical lifetime or lifecycle | | --- | --- | | Most normalized provider observations | Seven-day expiry, cleaned hourly | | Aircraft tracks and GPS interference | 30 minutes | | Naval vessel positions | 24 hours | | Stock index cache | One hour | | Prediction markets | Two-hour cache expiry, with separate market lifecycle reconciliation | | FININT indicators | Provider-dependent, from two hours to 45 days | | User Situation Room snapshots | 90 days | | Inactive incidents | Automatically resolved after seven days without observations | Y2 report, intel discovery, and intel expansion observations are excluded from the normal transient observation cleanup because other evidence records can depend on them. Their own `expiresAt` value does not make them subject to that hourly deletion path. API and map queries return the records that remain in their active storage path. If you need a durable external history, capture the relevant API results in your own governed store instead of relying on a short-lived Y2 cache. ## How the app reads the data The Situation Room combines several queries rather than loading one monolithic dataset: - normalized observations are filtered by source, category, severity, geography, and event time; - tracking and financial layers read their specialized tables; - country views combine live calculations with curated country and infrastructure context; - reference overlays are assembled from app registries and approved external display endpoints; - the Sources page reads circuit-breaker records separately from map data. This is why two surfaces can update at different times. For example, a source card can report a healthy provider while a specialized layer is empty because its rows expired, and a static overlay can remain visible while no runtime provider call exists for it. ## Next steps Compare providers, schedules, health semantics, and retention Learn how the map combines live, cached, and reference layers Understand the country-level synthesis shown in the map --- # Situation Room (/docs/osint) The Situation Room is Y2's map-based workspace for combining current OSINT events, Y2 report findings, network threats, reference infrastructure, tracking data, live media, and financial indicators. Situation Room access requires an active **Lite**, **Pro**, or **Elite** workspace. Free workspaces see an upgrade wall. All three enabled plans currently receive the same 50-layer selection limit and one-minute refresh entitlement. ## Open the monitor ### Select Situation Room Open **Situation Room** in the signed-in app. `/app/osint` redirects to the monitor at `/app/osint/monitor` after Y2 verifies the active workspace's access. ### Choose a map view Use the map controls to switch between the 3D globe and 2D Mercator map, and between the default and satellite basemap. The monitor starts in the global globe view. ### Set the event window Choose **1H**, **6H**, **24H**, **48H**, **7D**, or **ALL**. The default is 24 hours. ### Select layers Use the rail on the left to toggle layer entries. Press `L` outside a text field to collapse or expand the sidebar. New users start with every registered entry selected, and later changes are saved to their map preferences. ### Inspect the map Select an event or tracking marker for its source and context. Select a country polygon to open the country intelligence card. Press Escape to close the selected country. In either map view, select a grouped marker to open its observation list, then choose an observation for details. Several observations can share the same coordinates. ## Understand the map controls ### Time range The time bar changes the minimum timestamp sent to the cached map-event query. It applies to event markers such as earthquakes, conflicts, weather, fires, and Y2 report events. Static reference overlays—such as cables, bases, facilities, ports, cameras, and satellite assets—do not become historical merely because the event window changes. Live tracking tables also use their own freshness and expiry rules. ### Regions and theaters The header includes **Global**, six geographic theaters, and two thematic views: | Type | Views | | --- | --- | | Geographic | MENA, Africa, Latin America, Asia-Pacific, Europe, North America | | Thematic | Energy & Resources, Government / Policy | Selecting a header view moves the camera to its configured viewport. It does not replace the active layer selection. The desktop Theater Intel dock separately groups recent events into the same eight theater definitions. Its headlines follow the selected time range and refresh independently of map zoom, panning, and enabled layers. ### Loading and refreshing observations The monitor loads stored observations matching your map area, time range, and enabled event layers in pages. Progress shows how many observations have loaded. A completed selection means the matching stored data has loaded; upstream source collection can still be delayed or incomplete. Panning or zooming within the loaded coverage reuses those observations. Moving beyond it loads new coverage after the map stops moving. Completed selections refresh after five minutes, and you can refresh manually sooner. If loading stops, use retry to continue; a failed refresh keeps the previous completed map visible. ### Location and cursor context The desktop header can request browser geolocation and center the map on the result. It also shows the current cursor coordinates. Y2 uses marker, cursor, theater, and time-window context to prepare queries in the API tool; it does not send browser location until you request it. ## Layer registry The current registry contains 27 selectable entries across seven groups. **Y2 Report Events** maps event observations extracted from generated Y2 reports and preserves report/profile provenance when available. **Earthquakes**, **Conflicts**, **Political Events**, **Economic Events**, **Weather & Climate**, **Health & Pandemic**, **Cyber Threats**, **Maritime Events**, **Satellite Fires**, and **Military Aviation** display categorized rows from the OSINT event cache. **Live Malware** adds recent URLhaus and Feodo-derived threat observations and connects them to the Network Intel panel and relationship graph. **All Satellites** is the aggregate control. Its five subtype entries are **Starlink / Comms**, **Military / Intel**, **GPS / Navigation**, **Earth Observation**, and **Stations / Telescopes**. **CCTV Cameras** displays configured public camera assets. Selecting a marker opens the camera viewer; individual streams can still be unavailable at their upstream source. **Undersea Cables**, **Nuclear Facilities**, and **Ports & Chokepoints** are reference overlays. They have minimum zoom thresholds, so zoom in when a selected layer appears empty. **Aircraft Tracks**, **Naval Vessels**, **GPS Interference**, **Conflict Zones**, and **Military Bases** combine tracking tables with static reference overlays. “All Satellites” and its five subtypes are related controls. The 27 count describes selectable registry entries, not 27 independent upstream feeds. When every entry is enabled, the map still may not show a marker for every layer. A source can have no current rows, a row can fall outside the selected time window, a reference layer can require a closer zoom, or an upstream integration can be unavailable. ## Current tracking status The tracking status widget appears only when at least one tracking source has usable data or a recorded aircraft sync timestamp. | Layer | Current ingestion state | | --- | --- | | Aircraft Tracks | The Wingbits aircraft cron has been disabled since May 8, 2026 to stay within the shared request budget. The layer remains registered but automatic aircraft scans are not running. | | GPS Interference | Wingbits GPS zones run on cadence tiers ranging from hourly hot zones to a daily verification sweep. | | Naval Vessels | USNI fleet data syncs daily; the upstream fleet report itself updates less frequently. | Do not interpret an empty Aircraft Tracks layer as proof that no aircraft are present. It currently reflects disabled scheduled ingestion, not a negative intelligence finding. ## Country cards Select a country to open its intelligence card. Depending on available data, the card combines: - A country instability index and recent change. - An AI-generated or cached country brief. - A seven-day event timeline and active signals. - Infrastructure exposure from reference datasets. - News attributed to the country, prediction markets, and a stock-index indicator. - A text export assembled in the browser. Country cards combine static country profiles with live queries, so one section can have data while another is empty. See [Country Intelligence Cards](/docs/osint/country-cards) for field and scoring details. ## Desktop analysis panels The desktop monitor adds controls that are intentionally hidden on screens below the medium breakpoint. ### Right-side tool rail Lists countries or regions whose computed instability index is rising, sorted by delta. Explores recent malware infrastructure and can open related indicators, entities, and network context. Searches ontology entities after two characters and opens a live relationship graph for a selected result. Builds OSINT area, signal, and report requests from the selected marker, cursor, region, and time range. The API Query tool produces URLs and `curl` snippets. It does not bypass authentication. Creating new API keys and using scoped REST access requires Pro or Elite even though Lite can use the in-app Situation Room. ### Bottom dock The resizable desktop dock contains: - **Live media:** eight configured YouTube news channels, loaded muted by default. - **Theater Intel:** recent cached events across six geographic and two thematic panels, queried independently of the map's visible markers. Failed loads or refreshes offer a retry action. - **FININT:** market, energy, and macro indicators with recent values and detected signals. These panels are not rendered on mobile; the map, layer selector, time range, markers, tracking status, cameras, and country card remain the primary mobile surfaces. ## Data and source health Map data is a mixture of scheduled third-party syncs, Y2-derived observations, typed intelligence tables, and versioned static reference assets. “Live” means the interface updates reactively when stored data changes; it does not mean every upstream provider streams continuously or has the same refresh interval. Use **Sources** in the monitor header to inspect the source registry, last-success state, record counts, errors, and dataset-specific notes. See [Intelligence Sources](/docs/osint/sources) and [Data Infrastructure](/docs/osint/data-infrastructure) before treating freshness across layers as uniform. ## Entitlements and unfinished management surfaces Lite, Pro, and Elite currently share these Situation Room entitlement values: | Limit | Value | | --- | ---: | | Selected map layers | 50 | | Alert rules | 100 | | Countries per watchlist | 50 | | Refresh interval | 1 minute | | Snapshot retention | 90 days | The backend includes authenticated mutations for watchlists and alert rules and enforces these limits. The current Situation Room monitor does not expose a watchlist or alert-rule management screen, so do not rely on the quota table as evidence that those app workflows are available. Elite additionally enables team seats and audit logs in your workspace. Review [Plans and Limits](/docs/plans) for the surrounding profile, API, and workspace differences. ## Next steps Identify which providers and datasets back each monitor surface. Follow ingestion, normalization, identity, and API representations. Interpret country-level scores, events, markets, and exports. Use scoped REST endpoints outside the monitor. Compare Y2 with Maltego, Flashpoint, Recorded Future, Palantir, and MISP by use case. --- # Intelligence Sources (/docs/osint/sources) The Situation Room combines scheduled upstream feeds, event-driven Y2 observations, specialized tracking and market tables, and curated reference overlays. These source classes have different refresh and health semantics; a layer name does not imply a continuously streaming provider. ```mermaid flowchart LR A[Scheduled provider] --> B[Source adapter] C[Y2 workflow] --> D[Internal observation] B --> E[Normalized storage] D --> E F[Curated reference asset] --> G[Map overlay] E --> H[Map, cards, panels, API] G --> H B --> I[Circuit-breaker status] ``` ## Inspect source status ### Open Situation Room Open the monitor, then select **Sources** in the header. The page requires the same Lite, Pro, or Elite Situation Room access as the monitor. ### Identify the source class **Runtime feeds** display circuit-breaker state. **Map overlays and reference networks** are catalog entries and do not have the same fetch-health record. ### Read the timestamps and errors Runtime cards show the last successful fetch, consecutive failure count, current state, last error when present, and related map-layer IDs. ### Confirm source-specific freshness Compare the card with the schedules below. A closed circuit means requests are allowed; it does not prove that every row is recent or that every endpoint inside an aggregate source succeeded. ## Scheduled upstream feeds The following jobs are registered in the current Convex cron configuration. | Source | Stored data | Current schedule | | --- | --- | --- | | USGS | Earthquakes | Twice hourly at :05 and :35 | | GDACS | Global disaster alerts | Twice hourly at :05 and :35 | | NASA EONET | Natural-event observations | Twice hourly at :05 and :35 | | OSINT RSS | Categorized headlines from 24 feeds | Twice hourly at :07 and :37 | | NASA FIRMS | Active fire detections for configured regions | Twice hourly at :10 and :40 | | Polymarket | Prediction markets | Every 15 minutes | | Kalshi | Prediction markets | Twice hourly at :24 and :54 | | URLhaus and Feodo Tracker | Malware URLs and command-and-control infrastructure | Twice hourly at :12 and :42 through a serialized work pool | | NVD | CVE changes | Every 6 hours at :15 | | CISA KEV | Known Exploited Vulnerabilities catalog | Daily at 04:20 UTC | | FRED | Macroeconomic indicators | Twice hourly at :14 and :44 | | Yahoo Finance markets | Market, commodity, FX, crypto, rate, and volatility indicators | Every 15 minutes at :04, :19, :34, and :49 | | EIA spot series | WTI, Brent, and Henry Hub series | Hourly at :21 | | EIA release series | Weekly and monthly energy series | Daily at 14:30 UTC | | EIA nuclear outages | Facility outage snapshot | Daily at 14:45 UTC | | EIA grid load | Six US balancing-authority series | Hourly at :33 | | EIA crude imports | Per-port monthly data | Monthly on day 1 at 15:45 UTC | | USNI Fleet Report | Naval vessel disposition | Daily at 06:20 UTC | Schedules describe when Y2 attempts a fetch. The upstream dataset can update more slowly, return no rows, rate-limit a request, or be unavailable. ## Map result coverage The monitor loads all matching stored event observations for the visible area, selected time range, and enabled event layers. It continues through pages automatically; 500 is a page size, not a total result limit. The displayed count increases while loading, and completion is shown only after the final page. Changing the area or filters starts a new scan. Both globe and flat views group nearby observations and show their counts. Select a group to zoom in; observations at the same location can be selected individually from a paginated list. Full observation details load when selected. The time window stays fixed during each scan. A completed view refreshes after five minutes, or immediately with **Refresh now**. If a refresh fails, the previous completed view remains visible and **Retry loading** resumes retrieval. A completed scan describes stored observations; it does not establish complete upstream coverage. FIRMS collection failures and delays are shown separately. Live tracking, market, malware, and reference overlays retain their own query and refresh behavior. ## Hazards and earth observation The adapter reads the USGS earthquake feed for global magnitude 2.5+ observations and stores magnitude, depth, coordinates, time, and the provider's tsunami indicator. Category and severity normalization drive the Earthquakes layer. GDACS contributes global disaster alerts such as earthquakes, cyclones, floods, volcanic events, and drought-related observations. Provider alert context is normalized into Y2 event fields. EONET contributes earth-observation events such as storms, fires, volcanic activity, and other natural phenomena. Rows can feed the weather and fire views when coordinates and categories are available. FIRMS fetches active thermal anomalies for nine configured regional bounding boxes. The adapter requests today's and yesterday's observations from Suomi-NPP VIIRS, NOAA-20 VIIRS, NOAA-21 VIIRS, and Terra/Aqua MODIS. Qualifying detections are stored in batches without truncating each regional result set. Acquisition times are recorded in UTC, and partial collection failures are reported in source health. The cache retains new or changed detections for seven days after collection; identical repeated detections do not extend retention. New collection requests cover only today and yesterday; they do not recover older gaps or extend coverage beyond those nine regions. Low-confidence detections are excluded. Thermal detections are observations, not proof of conflict activity or cause. ## OSINT RSS headlines The OSINT map pipeline has its own 24-feed registry, separate from the 40-topic News Signal Feed. It includes: - Al Jazeera, Middle East Eye, and Al Monitor - BBC Africa and AllAfrica - BBC Latin America, InSight Crime, Mexico News Daily, and Reuters Americas - BBC Asia and South China Morning Post - BBC Europe and EUobserver - BBC World - New York Times World - Reuters World - OilPrice - BleepingComputer and The Hacker News - WHO News, WHO Disease Outbreaks, ProMED-Mail, ReliefWeb Health, and CDC MMWR The adapter infers category and severity from source defaults plus headline and description keywords. It also attempts place and incident resolution only for items that have coordinates. Feed requests run in batches of five with a ten-second timeout per feed. The Sources page reports one aggregate `rss` circuit. A sync is treated as successful when at least one of the 24 feeds succeeds, so a closed RSS circuit does not guarantee that every feed was available during the last run. ## Cyber intelligence Y2 stores multiple cyber source types with different cadences and downstream representations. | Source | What it contributes | Important boundary | | --- | --- | --- | | URLhaus | Active malware distribution URLs and related host indicators | Each sync is capped by the adapter and resolved into ontology entities when possible | | Feodo Tracker | Botnet command-and-control IP or host infrastructure | Shares the serialized cyber ingestion work pool with URLhaus | | NVD | Vulnerability records updated in the last-modified window | Six-hour delta ingestion; it is not the same as a full NVD backfill | | CISA KEV | Vulnerabilities known to be exploited | Daily catalog sync; KEV status is an exploitation signal, not a severity score by itself | URLhaus and Feodo observations power the Live Malware and network-intelligence surfaces. NVD and CISA KEV also feed the broader Cyber experience and ontology. Free workspaces can use the in-app Cyber surface, while Situation Room access still requires Lite or higher. New scoped API keys require Pro or Elite. ## Markets and financial intelligence ### Prediction markets Polymarket and Kalshi records are normalized into the prediction-market table and also written to the ontology market model when subject entities can be resolved. Market questions are associated with countries using provider metadata and configured text matching. A market probability is a market price, not a verified forecast or Y2 confidence score. Review liquidity, resolution terms, end date, and source URL before using it as evidence. ### FININT feeds The desktop FININT panel groups normalized indicators into Markets, Energy, and Macro segments. FRED, the scheduled Yahoo Finance market adapter, and the EIA family use the cadences in the table above. Country cards use a separate Yahoo Finance index adapter. When requested, it fetches the configured primary index for the selected country, calculates an approximately five-trading-day change, and caches that result for one hour. That on-demand country-index lookup does not have its own recurring cron. In-app FININT is available in Lite, Pro, and Elite. New `intel:finint` API keys require Pro or Elite; existing Lite keys can retain compatibility limits while the workspace remains on Lite. ## Military and navigation tracking ### Aircraft Tracks The Wingbits aircraft adapter and map layer remain in the repository, but scheduled aircraft scans have been disabled since May 8, 2026. Re-enabling requires both cron entries and the `WINGBITS_AIRCRAFT_TRACKING_ENABLED` environment flag. The `wingbits` source-health card is shared with GPS-interference ingestion. It can look healthy because GPS jobs are succeeding even though automatic aircraft tracking remains disabled. ### GPS Interference Wingbits navigation-accuracy observations are stored as H3 zones with sample count, aircraft count, average navigation accuracy, and normalized severity. The 22 configured theaters use four cadence tiers: | Tier | Cadence | Coverage count | | --- | --- | ---: | | Hot | Hourly | 6 theaters | | Watch | Every 3 hours | 8 theaters | | Perimeter | Every 6 hours | 5 theaters | | Verification | Daily | 3 theaters | GPS zones expire after 30 minutes. A lower-cadence theater can therefore have no current map row between scans; absence is not evidence that interference stopped. ### Naval Vessels The USNI adapter ingests fleet-report-derived vessel positions once daily. Stored rows expire after 24 hours and include vessel class, status, region, strike-group context when present, and a confidence label. The upstream report itself is not a live AIS stream. ## Internal and recognized source types The Sources screen also lists identifiers that are not independent scheduled provider feeds: `y2_report` observations are created from generated Y2 reports and signals. They preserve report and profile context where available and power the Y2 Report Events layer. `intel_discovery` and `intel_expansion` observations are created by Y2 intelligence workflows. They represent derived platform activity rather than third-party source polling. `manual` identifies operator-entered rows. Its presence in the source-type schema does not create an ingestion schedule. `acled`, `gdelt`, and `opensky` remain accepted source identifiers and appear in the in-app catalog. The current cron file has no scheduled ACLED, GDELT, or OpenSky ingestion job. Treat their cards as compatibility or planned-source descriptors unless a runtime status row proves that another in-scope process populated them. ## Reference overlays and media Reference cards do not use runtime circuit-breaker health. They describe data bundled into or referenced by the frontend: - Curated satellite category anchors for the current Space toggles. CelesTrak and SatNOGS are listed as candidate/reference catalogs; the map is not presenting live orbital propagation from those providers. - Curated undersea-cable routes with Submarine Cable Map lineage. - Y2-maintained strategic ports, military bases, and nuclear-facility inventories. - Configured public CCTV network assets. - Eight YouTube live-news channels used by the desktop media panel. Reference overlays can age independently of runtime feed health. Validate important facility, route, ownership, and status details against the relevant primary source before operational use. ## Normalization and retention ### Check the circuit A scheduled adapter first asks whether its source circuit allows a request. ### Fetch and normalize Source-specific code maps provider fields to typed observations or specialized tracking, market, vulnerability, and FININT rows. Primary adapters attach their registered data-source ID and provenance tier when available. ### Resolve context Where supported, Y2 resolves places, entities, incidents, market subjects, and evidence links. Resolution failure can be non-fatal, leaving the source observation available without every ontology link. ### Upsert deterministically Event-cache rows use the `(sourceType, sourceId)` identity. Duplicate items within a batch collapse with the last value winning; existing rows are updated rather than blindly appended. ### Publish reactively Convex queries update monitor markers, cards, panels, and app tools when stored data changes. Transient `osintDataCache` observations normally receive a seven-day expiry. Evidence-bearing Y2 report, discovery, and expansion observations are excluded from the transient cleanup set. Specialized tables have independent retention—for example, GPS and aircraft rows use 30 minutes, vessels use 24 hours, and the country stock-index cache uses one hour. ## Interpret circuit-breaker state | State | Meaning in the current UI | | --- | --- | | Pending | No circuit-breaker row exists for that source type; often means it has never recorded a fetch result | | Closed | Requests are allowed. The card can still show 1–4 accumulated failures before the threshold opens the circuit | | Open | Five or more consecutive failures have opened the circuit; requests are skipped until the five-minute cooldown expires | | Half open | A probe state recognized by the schema and UI; the next successful result closes it and a failed probe reopens it | After cooldown, an open source is allowed to attempt another request. A success resets the failure count and last error; another failure keeps the circuit open. Source health describes adapter execution, not truthfulness, completeness, or corroboration of the underlying data. Always retain the source URL and provenance when making a consequential assessment. ## Next steps Relate each source class to its current map or analysis surface. Follow identity, ontology, evidence, and API representations after ingestion. See how source records combine at country level. Query supported public representations with scoped credentials. --- # Plans and Limits (/docs/plans) Y2 resolves feature access and quotas from the active workspace. Switching workspaces can change the features available in the app and API even when the same person is signed in. Workspaces have separate plans, resources, API keys, and usage. Confirm the active workspace before troubleshooting a missing feature or unexpected limit. ## Pricing and workspace model | Plan | Price | Workspace | Best fit | |---|---:|---|---| | Free | Free | 1 seat | Follow one community profile | | Lite | $5/month or $50/year | 1 seat | Ongoing in-app intelligence and webhook delivery | | Pro | $20/month or $200/year | 1 seat | Scheduled delivery, API integrations, audio, and advanced publishing | | Elite | $200/month or $2,000/year | 5 seats, including the owner | Shared intelligence operations with governance controls | Elite includes 10× Pro’s credits, custom and total profile allowances, Automation definitions, API keys and request limits, branding templates, and pronunciation dictionaries. These allowances are shared across the workspace. Elite includes five seats, with larger organizations available by arrangement. Situation Room coverage, refresh frequency, and retention are the same as Pro. Unlimited delivery remains unlimited. ## Projects and Automations | Capability | Free | Lite | Pro | Elite | |---|---:|---:|---:|---:| | Private Projects | No | Yes | Yes | Yes | | Project graph, timeline, evidence, and knowledge | No | Yes | Yes | Yes | | Automations | No | No | Up to 10 | Up to 100 | | Schedule and profile-completion triggers | No | No | Yes | Yes | | Project-bound Automation output | No | No | Yes | Yes | | User-bound Project and Automation audit | No | No | No | Yes | Project access starts with Lite. Automations require Pro or Elite, and their definition quota is shared by the workspace. Projects and Automation definitions are owner-private in this release; an Elite workspace does not automatically share one member's Project content with another. ## Profiles and delivery | Capability | Free | Lite | Pro | Elite | |---|---:|---:|---:|---:| | Custom profiles | 0 | 1 | Up to 5 custom profiles | Up to 50 custom profiles | | Community subscriptions | 1 | 3 | Up to 20 total profiles | Up to 200 total profiles | | Total profiles | 1 | 4 | 20 | 200 | | Report deliveries | Community subscription only | Unlimited | Unlimited | Unlimited | | Email delivery | Yes | Yes | Yes | Yes | | Webhook delivery | No | Yes | Yes | Yes | | SMS delivery | No | No | Yes | Yes | | Scheduled reports dashboard | No | No | Yes | Yes | | Branding templates | 0 | 0 | 10 | 100 | | Audio narration | No | No | Yes | Yes | | Pronunciation dictionaries | 0 | 0 | 10 | 100 | Free includes one community-profile subscription. Creating a custom profile and configuring its report delivery starts with Lite. ## Agent Y2 and research | Capability | Free | Lite | Pro | Elite | |---|---:|---:|---:|---:| | Agent Y2 credits per month | 0 | 100 | 1,000 | 10,000 | | Web search tool | No | Yes | Yes | Yes | | Image generation | No | Yes | Yes | Yes | | Advanced research | No | Yes | Yes | Yes | | News Terminal | Yes | Yes | Yes | Yes | ## API access | Capability | Free | Lite | Pro | Elite | |---|---:|---:|---:|---:| | Create new API keys | No | No | Up to 10 | Up to 100 | | Default rate limit per minute | — | — | 30 | 300 | | Default rate limit per day | — | — | 5,000 | 50,000 | | Webhook management | No | Yes | Yes | Yes | | x402 pay-per-request | Supported endpoints | Supported endpoints | Supported endpoints | Supported endpoints | Lite cannot create new API keys. Keys that existed before the Pro requirement retain a compatibility limit of 10 requests per minute and 500 requests per day while the workspace remains on Lite. ## Situation Room and Intel workspaces | Capability | Free | Lite | Pro | Elite | |---|---:|---:|---:|---:| | Cyber dashboard | Yes | Yes | Yes | Yes | | Situation Room | No | Yes | Yes | Yes | | FININT workspace | No | Yes | Yes | Yes | | Explorer investigation boards | No | Yes | Yes | Yes | | Investigation sharing | No | No | Yes | Yes | | Map layers | 0 | 50 | 50 | 50 | | Alert rules | 0 | 100 | 100 | 100 | | Watchlist countries | 0 | 50 | 50 | 50 | | Snapshot retention | — | 90 days | 90 days | 90 days | ## Organization capabilities | Capability | Free | Lite | Pro | Elite | |---|---:|---:|---:|---:| | Organization workspace | No | No | No | Yes | | Workspace members | 1 | 1 | 1 | 5 included; expansion by arrangement | | Workspace audit logs | No | No | No | Yes | | Workspace API-key oversight | No | No | No | Yes | ## Choose the next guide Set up a topic, cadence, and delivery path within the active workspace limits. Deliver reports to an HTTPS endpoint on Lite, Pro, or Elite. Configure Pro or Elite credentials and scopes for API requests. Understand workspace switching, resource ownership, and Elite team governance. Organize chats, evidence, private knowledge, graphs, and reports on Lite or higher. Run bounded Agent Y2 work from Pro and Elite workspace triggers. --- # Branding Templates (/docs/profiles/branding) Branding templates let a Pro or Elite workspace reuse its logo, colors, terminology, and delivery copy across intelligence profiles. Branding requires an active **Pro** or **Elite** workspace. Pro supports up to **10** templates; Elite supports up to **100**. ## Create and assign a template ### Open Branding In the app, go to **InfoOps → Branding**, then select **New Template**. ### Name the template Use a name that identifies where the branding belongs, such as “Company Briefings” or “Client Deliverables.” A template name can contain up to 50 characters. You can also mark the template as the workspace default. Only one template can hold that marker at a time. ### Configure the output Add only the elements you need. Any omitted values fall back to Y2 defaults. | Area | Available settings | | --- | --- | | Visual identity | Logo, logo alt text, header color, accent color | | Terminology | Report type and analyst role | | Delivery copy | Email preview prefix, SMS prefix, audio intro, audio outro | | Static content | Optional content before or after a report | ### Save the template Resolve any validation errors, then select **Save**. ### Assign it to a profile Create or edit a profile, open **Advanced**, select the branding template, and save the profile. Branding is applied when that profile is rendered or delivered. The **Set as Default** marker is unique, but the current profile editor still requires you to select a branding template explicitly. Verify the assignment in **Advanced** instead of relying on the default marker alone. ## Visual identity ### Logo The branding editor accepts PNG, JPEG, SVG, and WebP files up to 2 MB. Add meaningful alt text for recipients who cannot see the image. | Property | Enforced limit | | --- | --- | | File size | 2 MB | | Logo alt text | 100 characters | Use a wide logo with a transparent background when possible, then preview a delivered email to confirm that it remains legible at small sizes. ### Colors Header and accent colors accept three- or six-digit hex values, such as `#05c` or `#0055cc`. Values are normalized when the template is saved. Use only hex colors. Invalid values are rejected by server-side validation. ## Terminology and delivery copy Use `{name}` in supported text fields to insert the assigned profile's name. For pronunciation rules that apply to names, acronyms, or technical terms, use a [pronunciation dictionary](/docs/audio#create-a-pronunciation-dictionary). ## Add static content Each template can place one content block before the report and another after it. A block can use the `callout`, `highlight`, or `sponsor` style and can contain channel-specific copy. | Content | Enforced limit | Used by | | --- | ---: | --- | | Label | 30 characters | Block heading | | HTML | 500 characters | Email and rendered report | | Audio text | 200 characters | Audio narration | The `{name}` placeholder is supported in the label, HTML, and audio text. Server-side limits control what can be saved. Keep static HTML at or below 500 characters, audio text at or below 200, and labels at or below 30 even if an editor counter displays a larger allowance. Static HTML is sanitized before it is stored and rendered. Scripts, embedded forms, event handlers, unsafe URLs, iframes, and similar executable elements are removed. Use simple formatting and links rather than interactive markup. ## Update or remove a template Assigned profiles reference the template rather than copying it. Changes therefore affect subsequent renders and deliveries from every profile using that template. To delete a template: ### Find assigned profiles Edit each profile that uses the template. ### Remove or replace the assignment Select another template or clear the branding selection, then save the profile. ### Delete the template Return to **InfoOps → Branding**, select **Delete**, and confirm. The backend rejects deletion while any profile still references the template. Unassign every profile first, even if the confirmation dialog suggests that profiles will fall back automatically. ## API boundary The public profile create and update schemas expose `brandingTemplateId`. Use the generated [Profiles API reference](/docs/api/reference/profiles) for the canonical request schema and required scopes. Creating, editing, and deleting branding templates is currently an app-only workflow; dedicated branding-management endpoints are not part of the public OpenAPI document. ## Plan limits | Plan | Templates | Logo upload | | --- | ---: | --- | | Free | 0 | No | | Lite | 0 | No | | Pro | 10 | Yes | | Elite | 100 | Yes | ## Next steps Configure a profile and assign the template. Review how generated reports reach recipients. Compare workspace capabilities and quotas. --- # Create a Profile (/docs/profiles/creating) Use the profile editor to define a recurring intelligence question, structure its reports, and configure how Y2 researches and schedules it. Sign in, complete onboarding, and select the workspace that should own the profile. Profile capacity belongs to the active workspace; see [Plans and limits](/docs/plans). ## Editor workflow The editor validates each step before moving forward. On desktop, the report preview updates as you change the profile. ## Create the profile ### Open the editor Open [My Profiles](/app/infoops/my-profiles), then select **Create Profile**. You can also open the [new profile editor](/app/infoops/profiles/new) directly. ### Complete Basic Information Enter the fields shown in the first editor step: Write the topic as a monitoring objective rather than a category name: ```text Monitor newly disclosed ransomware campaigns affecting US healthcare providers, including exploited vulnerabilities, named threat actors, and operational impact. ``` The editor may display a three-layer option, but report execution currently caps recursion at the root topic plus one child layer. Select the two-layer option for the deepest effective workflow. ### Design the report structure Choose a **General**, **Finance**, **Technology**, **Security**, or **Custom** template. Reorder, rename, add, or remove sections until the preview matches the decision the report should support. Add **Custom Instructions** when the topic alone does not express the required emphasis, tone, comparison, or exclusions. ```text Separate confirmed exploitation from unverified reporting. Prioritize patient-care impact, affected vendors, and mitigations with named owners or deadlines. ``` Switching from a custom section list to a preset replaces the custom sections after confirmation. ### Configure schedule and research In **Advanced Configuration**, review these settings: Choose **Daily**, **Weekly**, **Biweekly**, or **Monthly**, then select the required day and time. The editor stores and displays schedule times in UTC. Choose Basic or Advanced search, select a time range from one day to one year, and optionally add comma-separated domains to include or exclude. **Auto** lets the model decide when to use its active research tool. **Required** requires tool use. **None** disables tool calls for the run. Source Freshness applies the current recency preference and maximum-age settings. Link Validation checks whether selected links are accessible. Pro and Elite can enable audio narration, choose a voice and speed, apply a pronunciation dictionary, and select a branding template. Unavailable controls remain disabled on lower plans. Turn **Share with Community** off when the profile should remain workspace-only. When it is on, other users can discover and subscribe to the profile. ### Create and verify the profile Select **Create Feed**. Y2 creates the profile as active, schedules its next run, creates the workspace's subscription record, and returns you to **My Profiles**. Confirm that: - The profile appears in **My Profiles**. - Its name, topic, and cadence match the editor. - Its status is active. - The profile opens without an access or plan-limit error. ### Review delivery after creation Open the saved profile's delivery settings. New profile subscriptions use email when no other method is selected. Webhooks require Lite or higher; SMS requires Pro or Elite and a verified phone number. Delivery preferences are tied to the subscription, so they become available after the profile exists. ## Example configurations ```yaml name: Weekly Cyber Risk Brief topic: >- Monitor ransomware campaigns affecting US healthcare providers, including exploited vulnerabilities, threat actors, and operational impact. tags: [cyber, ransomware, healthcare] template: Security frequency: Weekly researchLayers: 2 timeRange: Week ``` ```yaml name: Critical Supplier Risk Monitor topic: >- Monitor disruption risks affecting semiconductor suppliers, East Asia ports, rare-earth inputs, and export-controlled components. tags: [supply-chain, semiconductors, logistics] template: General frequency: Daily researchLayers: 2 includeDomains: [reuters.com, cisa.gov] ``` ## Troubleshooting Make the name at least three characters and the topic at least ten characters. Validation messages appear below the affected field. Confirm the active workspace and compare its current profile count with [Plans and limits](/docs/plans). Limits include owned profiles and subscriptions. Confirm that the active workspace includes the feature. Audio, branding, and SMS require Pro or Elite; webhook delivery starts with Lite. Narrow the topic to a geography, industry, entity set, time horizon, or decision. Use Custom Instructions for output constraints and domain controls for source boundaries. ## Next steps Understand UTC timing, frequency-specific fields, and schedule changes. Verify the default delivery path and report format. Deliver completed reports to an HTTPS endpoint. Review visibility, subscriber behavior, and ownership before publishing a profile. --- # Information Operations (/docs/profiles) An Information Operations profile is the durable configuration behind a recurring Y2 briefing. It defines what to research, how to structure the result, when to run, and where the report should go. ## Default report writing Y2's default writing guidance follows the [Basset-Hound OSINT writing standards](https://github.com/Basset-Hound-OSINT/osint-resources/blob/main/knowledge/reporting/writing-standards.md): lead with the assessment and why it matters, distinguish sourced facts from judgments, explain confidence and its evidence basis, and address material alternatives, assumptions, and gaps. Reports use neutral language, unambiguous dates, and inline citations; Y2 adds source references. Your saved sections and custom instructions take precedence over the default layout, tone, language, and requested length. Detailed section guidance, bullets, and examples are passed to synthesis together. Existing profiles keep their saved structures, and optional domain templates remain available. The engine's HTML, citation, and minimum-completeness requirements still apply. Writing guidance directs generation; it is not a guarantee that each model judgment is correct. ## Profile lifecycle ```mermaid flowchart LR config["Topic, instructions, and research settings"] --> run["Research workflow"] run --> report["Structured intelligence report"] report --> workspace["Y2 workspace"] report --> delivery["Enabled delivery channels"] report -. "context for later cycles" .-> run ``` The profile persists between runs. Updating its topic, instructions, source-quality settings, or cadence changes later reports without requiring a new workflow each time. ## What a profile controls A topic, tags, and optional custom instructions define the question and desired emphasis. A template or custom section list defines how the result is organized. New profiles start with Bottom Line Up Front, Key Judgments, Background, Analysis, and Confidence Assessment. Search depth, time range, domain controls, source freshness, link validation, and recursive research tune how evidence is gathered. Daily, weekly, biweekly, or monthly timing controls when the profile runs. The active plan determines which delivery methods are available. ## Profile types Subscribing to a global or community profile does not transfer ownership. The original owner keeps the profile configuration; the subscriber controls their own subscription and supported delivery preferences. Profile ownership and subscriptions count against the active workspace. Free includes one community-profile subscription but no custom profiles. Creating a custom profile starts with Lite. See [Plans and limits](/docs/plans) for the full entitlement reference. ## Research depth and source quality Y2 supports a single-pass workflow and recursive research. When recursion is enabled, the runtime can identify subtopics and research them in parallel before final synthesis. Report generation currently caps recursive depth at one child layer: the root topic plus one layer of parallel subtopic research. A larger saved value does not create additional runtime layers. Source-quality settings can prefer recent results, apply a maximum source age, and validate links. These controls improve evidence selection, but they do not guarantee that an external source is complete or correct. ## Report structure Profiles can use a general, finance, technology, or security-oriented structure, or a custom list of sections. The first required section establishes the main conclusion; later sections add findings, analysis, risks, outlook, or other domain-specific context. Audio narration and pronunciation dictionaries are separate Pro and Elite capabilities. Branding templates can apply a consistent visual identity and terminology to supported reports. ## Choose the next task Configure a topic, report structure, research behavior, cadence, and initial delivery. Understand frequencies, UTC scheduling, and changes to future runs. Make an eligible personal profile discoverable to the community. Apply reusable visual identity and report terminology on supported plans. Choose narration voices and pronunciation dictionaries for generated reports. --- # Schedule Profile Reports (/docs/profiles/scheduling) Every active custom profile stores a UTC cron schedule. Y2 uses it to enqueue the next report run after the current run reaches a terminal state. ## Supported frequencies | Frequency | Current schedule behavior | Required input | |---|---|---| | Daily | Every day at the selected UTC time | Time | | Weekly | Every selected weekday at the selected UTC time | Weekday and time | | Biweekly | On the 1st and 15th of each month at the selected UTC time | Time | | Monthly | On the selected day of the month at the selected UTC time | Day of month and time | The current Biweekly schedule runs on calendar days 1 and 15. It is not a rolling fourteen-day interval and does not use the weekly day selector. A monthly schedule runs only when the selected date exists. For example, a profile scheduled for the 31st does not run in a thirty-day month. ## Set a schedule ### Open the profile editor Open [My Profiles](/app/infoops/my-profiles), select the profile, and choose its edit action. ### Open Advanced Configuration Move to the third editor step. Find **Delivery Schedule** at the top of the page. ### Choose a frequency Select Daily, Weekly, Biweekly, or Monthly. Weekly adds a weekday selector; Monthly adds a day-of-month selector. ### Choose the UTC time Select an hourly time from `00:00` through `23:00`. The editor and backend interpret the value as UTC. Convert the desired local time to UTC before saving. Repeat the conversion when your local timezone enters or leaves daylight saving time. ### Save and verify Select **Update Feed**, return to **My Profiles**, and confirm that the profile displays the expected next-run information. Changing the schedule of an active profile cancels its existing scheduled job, enqueues the new schedule, and starts a report workflow immediately. Make schedule changes when an extra run is acceptable. ## Generated cron values Y2 generates the cron expression; the profile editor does not accept a custom expression. ## Choose an effective cadence Use for topics where a missed day changes the decision: active incidents, market movement, or operational disruptions. Use for recurring reviews where developments should accumulate into a manageable briefing. Use when twice-monthly calendar reporting is acceptable and strict fourteen-day spacing is not required. Use for slower trend reviews, executive summaries, or stable watch areas. When several profiles serve the same reader, stagger their UTC times. This spreads research work and prevents multiple reports from arriving together. ## Failure and recovery behavior Report workflows use bounded action retries and a recovery watchdog for eligible interrupted or stalled runs. A failed synthesis does not proceed to audio or delivery as if it were complete. Recovery behavior does not change the profile's stored cadence. ## Next steps Verify where a completed report is sent by default. Send completed reports to an HTTPS endpoint. Return to the full editor workflow and research settings. --- # Share a Profile (/docs/profiles/sharing) Turn on **Share with Community** to make a workspace-owned profile discoverable to other Y2 users. Subscribers follow the same profile; sharing does not create a separate copy for each workspace. The current new-profile editor starts with **Share with Community** enabled. Turn it off in Advanced Configuration before selecting **Create Feed** when the profile should remain private. ## How sharing works ```mermaid flowchart LR owner["Owning workspace"] --> profile["Shared profile configuration"] profile --> catalog["Community discovery"] catalog --> subscriptions["Subscriber workspace records"] profile --> reports["Future profile reports"] reports --> subscriptions ``` The owning workspace controls the profile topic, research configuration, cadence, status, and community visibility. Subscriber workspaces control their own subscription and supported delivery preferences. Changes to the shared profile affect later reports for every active subscriber because subscribers reference the same profile. ## Before sharing Confirm that the profile: - Has a clear name and a topic that another reader can understand without private context. - Uses report sections and instructions appropriate for a broader audience. - Does not contain secrets, customer data, private names, or confidential domain restrictions. - Has produced a report you reviewed for scope, source quality, and usefulness. - Uses a cadence the owning workspace intends to maintain. Treat the profile name, topic, tags, custom instructions, and research configuration as public metadata when community sharing is enabled. Do not use a shared profile as a secret store. ## Share an existing profile ### Open the profile editor Open [My Profiles](/app/infoops/my-profiles), select a profile owned by the active workspace, and choose its edit action. ### Review the public-facing configuration Check the name, topic, tags, section titles, and custom instructions. Remove internal-only details before changing visibility. ### Enable community sharing Open **Advanced Configuration** and turn on **Share with Community**. ### Save and verify Select **Update Feed**, then open [Community](/app/infoops/community). Search for the profile name and confirm that it appears with the expected Global or Community classification. ## What subscribers can do Subscribers can: - Discover and open global or community profiles. - Add a profile to the active workspace when capacity remains. - Remove their own subscription without changing the shared profile. - Configure a supported delivery method for their subscription. They cannot edit or delete the owning workspace's profile. Subscription limits belong to the active workspace: Free supports one external community or global subscription, Lite supports three, and Pro or Elite count subscriptions and owned profiles against their total profile capacity. See [Plans and limits](/docs/plans). ## Stop sharing ### Edit the shared profile Open the profile from **My Profiles** and move to Advanced Configuration. ### Disable community sharing Turn off **Share with Community**, then select **Update Feed**. ### Confirm discovery is disabled Search for the profile in **Community**. It should no longer be available for a new subscription. Disabling community sharing removes the profile from public discovery, but it does not automatically delete subscription records that already exist. Deleting the profile removes its subscriptions and associated generated resources; treat deletion as a separate destructive action. ## Maintain a useful shared profile Make focused refinements. Replacing the subject entirely can surprise existing subscribers. Add concise domain, geography, industry, and cadence tags that improve discovery. Check whether sources and conclusions still match the published scope before expanding it. Y2 does not currently promise an automatic subscriber notification for profile edits. Avoid silent changes that materially alter the briefing. ## Next steps Understand the discovery and subscription experience from the reader's perspective. Refine the topic, sections, source controls, and research behavior. Choose where completed reports should arrive. --- # Create Your First Project (/docs/projects/getting-started) This tutorial creates a Project in the active workspace, attaches one existing resource, and starts a chat whose context stays inside that Project. ## Before you begin You need: - A Lite, Pro, or Elite active workspace. - An owner, admin, or member role with write access. Elite viewers are read-only. - At least one report, profile, graph board, or generated image in the same workspace if you want to attach an existing resource. ## 1. Confirm the active workspace Use the workspace switcher and select the workspace that should own the Project. Plan access and resource choices are resolved from this workspace. ## 2. Create the Project 1. Open **Projects** in the app navigation. 2. Select **New Project**. 3. Enter a short name. 4. Add an objective that describes the decision or investigation the Project should support. 5. Optionally add standing instructions for Project-scoped AI work. 6. Save the Project. The new Project starts active with a private context policy. Its initial workspace view opens the Graph, Timeline, Situation, Evidence, Knowledge, and Overview tabs. ## 3. Attach an existing resource Open **Overview**, search the resource picker, and attach a report, profile, graph board, or generated image. The picker shows only resources owned by you in the active Project workspace. Attaching a report automatically preserves its stored citations as Project evidence. Attaching a profile does the same for every prior report in that profile and keeps preserving citations from future reports. Supported report intelligence can also schedule a graph projection. ## 4. Start a Project chat Select **New chat** from the Project. Messages written in that thread carry the Project and workspace identifiers. If you later switch workspaces, Y2 hides the thread until its workspace is active again. ## 5. Verify the result Return to **Overview** and confirm that: - The resource appears under Project resources. - The thread appears under recent Project chats. - Timeline activity records the changes. Create a Project-bound [Automation](/docs/automations/creating) when the objective needs a scheduled or report-completion follow-up. Lite Projects remain fully usable without Automations. --- # Projects (/docs/projects) Projects are private working contexts for a single intelligence objective. A Project connects related chats, reports, profiles, graph boards, generated images, evidence, and private documents without mixing them into another workspace or the global knowledge corpus. Projects use the active workspace's plan. Free workspaces can see the upgrade path but cannot read or change Project data. ## The Project boundary Every Project has both a `workspaceId` and an `ownerId`. In this release, that means: - The Project can use only resources owned by the same user in the same active workspace. - Project chats and private knowledge stay attached to that Project. - Switching workspaces removes access until the Project's workspace is active again. - Other Elite workspace members do not automatically receive access. Project sharing is not yet supported. Y2 checks the boundary again for background indexing, graph projection, chat writes, and Automation runs. A saved identifier alone does not grant access after membership or plan access changes. ## Project views | View | Purpose | |---|---| | Graph | Maintain a persistent graph for the objective. | | Timeline | Review attached intelligence and Project activity in time order. | | Situation | Explore geolocated signals attached to the Project. | | Evidence | Preserve citations, captures, notes, tags, and lineage. | | Knowledge | Add private source documents for Project-scoped retrieval. | | Overview | Review attached resources, recent chats, and eligible Automations. | Project knowledge is separate from Global Knowledge. Removing a Project binding archives that binding; it does not publish the content globally. ## Projects and Automations Projects are available starting with Lite. [Automations](/docs/automations) require Pro or Elite. A Lite user can still use every Project view, but the Automation card presents the Pro upgrade path instead of querying protected Automation data. On Pro or Elite, an Automation can be bound to a Project. Its output thread and run context remain inside the same owner and workspace boundary. ## Elite governance Elite workspaces add user-bound audit records for material Project changes, including Project creation and settings changes, resource and evidence lifecycle actions, graph changes, chat moves, and tracking-rule changes. Audit records identify the acting user and workspace; they do not make private Projects visible to other members. ## Choose the next task Build a Project and add its first intelligence resource. Attach reports, preserve evidence, add knowledge, and move chats safely. Review access rules, supported resources, views, limits, and lifecycle behavior. --- # Organize Intelligence in a Project (/docs/projects/organizing-intelligence) Use this guide when a Project already exists and you need to keep its supporting material organized without crossing workspace boundaries. ## Attach reports and profiles Open **Overview**, use the resource search, choose a role, and attach the item. Supported resource types are reports, profiles, graph boards, and generated images. Y2 rejects an item that belongs to another owner or workspace, even if its identifier is supplied directly. Use roles consistently: - **Knowledge** for background material. - **Working** for active analysis. - **Output** for deliverables. - **Primary graph** for the main graph board. ## Preserve and classify evidence Open **Evidence** to preserve citations from linked reports, capture a web source, add notes and tags, or mark an item important. Evidence lifecycle history records archive, restore, and prune actions. Trackers can classify future evidence by exact domain or URL prefix. Pruning is intentionally stricter than archiving. Review downstream lineage before pruning a source that supports an assertion, report, graph object, or incident. ## Add private knowledge Open **Knowledge** and upload PDF, text, Markdown, Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, or CSV files up to 10 MB. PDFs must contain selectable text because scanned pages requiring OCR are rejected. Uploaded content is indexed only for authorized Project-chat retrieval; it is not added to the global knowledge corpus. Archive a binding when the source should stop participating in retrieval, and restore it when needed. ## Maintain the Project graph Open **Graph** to create the persistent graph, or save an Explorer graph into the Project. Saving again merges nodes and edges by identifier. A Project graph supports up to 250 nodes and 500 edges. ## Move an existing chat Move only a chat you own in the active workspace. The target Project must also be active and owned by you. Once moved, future message access and background Agent Y2 work are checked against the Project boundary. ## Archive completed work Archive the Project when the objective is complete. Archiving removes it from active lists and blocks active-only operations without deleting its history. Restore it from the archived Projects view when work resumes. --- # Projects Reference (/docs/projects/reference) ## Availability and access | Rule | Behavior | |---|---| | Minimum plan | Lite | | Workspace scope | The active workspace must match the Project workspace. | | Ownership | Owner-private in this release; team sharing is not supported. | | Write roles | Owner, admin, or member. Elite viewers are read-only. | | Context policy | `project_only` | | Elite audit | Material changes include the workspace and acting user. | ## Project fields | Field | Constraint or meaning | |---|---| | Name | Required, up to 80 characters. | | Objective | Optional, up to 1,000 characters. | | Instructions | Optional, up to 8,000 characters. | | Status | `active` or `archived`. | | Default view | Graph, Timeline, Situation, Reports, Evidence, or Knowledge. | | Default time window | 24 hours, 7 days, 30 days, or custom. | | Revision | Used to reject conflicting settings updates. | ## Supported resource types | Resource | Ownership check | |---|---| | Investigation graph board | Same owner and workspace. | | Profile | Same owner and workspace. | | Report | Its profile must have the same owner and workspace. | | Generated image | Same owner and workspace. | Resource roles are `knowledge`, `working`, `output`, and `primary_graph`. ## View and storage limits - Persistent graphs: 250 nodes and 500 edges. - Recent Project chat list: the app displays up to 12 threads. - Project instructions: 8,000 characters. - Graph title: 200 characters. - Private knowledge accepts PDF, text, Markdown, Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, and CSV files up to 10 MB. - PDFs are extracted page by page and must contain selectable text; scanned pages requiring OCR are rejected. - Private knowledge is capped at 100 documents per owner and workspace, 200 PDF pages, 2,000,000 extracted characters, and 512 indexed chunks per revision. - Evidence uses a separate archive and integrity contract; evidence attachments are not parsed as Project knowledge. ## Lifecycle Archiving is reversible. Project resources, chat association, evidence history, graph state, and private knowledge remain stored. Active-only mutations reject archived Projects until restored. Deleting an attached resource link does not delete the original report, profile, graph board, or image. Evidence pruning and knowledge-source deletion have separate confirmation and retention behavior in their respective views. --- # Use Y2 from the Command Line (/docs/sdks/cli) 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. ```bash title="Terminal" export PATH="$HOME/.y2/bin:$PATH" && curl -fsSL https://y2.dev/harness/install.sh | sh ``` This 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: ```bash title="Terminal" export PATH="$HOME/.y2/bin:$PATH" y2 --version ``` Pin an exact release when you need a reproducible install: ```bash title="Terminal" export PATH="$HOME/.y2/bin:$PATH" && curl -fsSL https://y2.dev/harness/install.sh | sh -s -- v0.0.7 ``` Upgrade an installed harness on the stable channel: ```bash title="Terminal" y2 upgrade ``` ## Save 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: ```bash title="Terminal" y2 auth ``` The 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: ```bash title="Terminal" 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: ```bash title="Terminal" cd your-project y2 ``` Run one noninteractive prompt with `y2 ask`: ```bash title="Terminal" 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: ```bash title="Terminal" y2 status --json y2 doctor ``` To use another OpenAI-compatible endpoint directly, provide its base URL, key, and model: ```bash title="Terminal" export OPENAI_BASE_URL="https://your-provider.example/v1" export OPENAI_API_KEY="your-provider-api-key" export Y2_MODEL="your-model-id" y2 ``` `OPENAI_BASE_URL` may include `/chat/completions`; otherwise y2 appends it. See the [Agent Y2 API guide](/docs/api/agent-y2) for the default endpoint and streaming contract. ## Use curl directly You need curl and, for JSON processing, jq: ```bash 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 ```bash title="y2.sh" #!/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: ```bash y2_get "/api/v1/news/feeds" | jq -r '.data[] | [.id, .name, .groupLabel] | @tsv' ``` Then request a bounded page: ```bash 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: ```bash 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 ```bash 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: ```bash title="export-report-index.sh" #!/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") done ``` Do not decode or edit the cursor. It is bound to the original filters and ordering. ## Export one report as Markdown ```bash : "${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: ```bash 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" done ``` For 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: ```bash 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 fi ``` Log `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: ```bash 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`. Agent Y2 uses SSE in the Vercel AI SDK data-stream format. curl can display raw frames, but an application should use a parser that understands the stream and retains `X-Thread-Id`. curl is useful for management endpoints, but testing delivery requires a public HTTPS receiver that preserves raw request bytes for signature verification. --- # Client Libraries and Tools (/docs/sdks) Y2 publishes an OpenAPI 3.1 contract that can drive types, generated clients, request tools, and tests. Treat that contract—not a package's convenience method names—as the compatibility boundary. New API-key access requires Pro or Elite. Existing grandfathered Lite integrations retain their existing keys during the grace period. Create a key in the active workspace and grant only the scopes the client needs. ## Choose an integration style Generate path and schema types, then call Y2 with a small typed `fetch` wrapper. Build a synchronous or asynchronous HTTP client around the published response contract. Install Y2 Information Dominance for agent workflows, or use curl and jq for direct API work. Explore path and query parameters, run live reads, and copy starter snippets. | Option | Best for | Contract fidelity | Main responsibility | | --- | --- | --- | --- | | Y2 Information Dominance | Interactive agent work and one-off prompts | Uses Agent Y2 or a configured OpenAI-compatible endpoint | Keep the harness and credentials current | | Generated OpenAPI client | Broad API coverage | Regenerate when the spec changes | Review generator behavior | | Generated types + native HTTP | Controlled production integrations | Types come directly from OpenAPI | Maintain a small transport layer | | curl or Workbench | Exploration and operations | Request is visible and explicit | Handle parsing and pagination | | Separately released package | Convenience APIs | Depends on its release date | Verify version and operation coverage | Package installation names, exported classes, and helper methods are release-specific and are not defined by the platform OpenAPI file. Before adopting a separately released client, compare its generated operation IDs and models with the current endpoint reference. ## Start from the contract Download the production description: ```bash curl --fail --silent --show-error \ "https://y2.dev/api/openapi.yaml" \ --output openapi.yaml ``` Then validate the assumptions that most often break generated clients: - v1 paths inherit `https://api.y2.dev/api/v1`; - Intel v2 operations override the server with `https://api.y2.dev`; - bearer scopes are declared in `x-required-scopes`; - supported reads can declare x402 as a second security mode; - errors use `application/problem+json` with stable top-level `code` and `requestId`; - list pagination uses `links.next` and `meta.page.nextCursor` where applicable; and - some endpoints can return NDJSON, GeoJSON, Markdown, plain text, audio redirects, or SSE. ## Use a minimal bearer request Every bearer client needs the same basic transport behavior: ```bash export Y2_API_KEY="y2_..." curl --fail-with-body \ "https://api.y2.dev/api/v1/news?topics=markets&limit=5" \ --header "Authorization: Bearer $Y2_API_KEY" \ --header "Accept: application/json" ``` Keep the API key on a server, worker, job runner, or local development machine. Do not ship it in a browser bundle or mobile application. ## Model the common response shapes ```mermaid flowchart TD A[HTTP response] --> B{Status 2xx?} B -->|No| C[Problem Details] B -->|Yes| D{Representation} D -->|Collection| E[data array + meta + links] D -->|Single JSON| F[data object + optional meta] D -->|Streaming or file| G[Endpoint-specific transport] ``` Do not assume every successful response is `{ data: [...] }`. A generated client must preserve declared media types and status codes, especially `204` deletes, `302` audio redirects, `text/event-stream`, `application/x-ndjson`, `application/geo+json`, and `text/markdown`. ## Regenerate safely ### Pin the input Generate from a reviewed copy of `https://y2.dev/api/openapi.yaml` or pin the generated output in source control. Avoid silently regenerating from a moving URL during every production build. ### Review operation IDs and servers Stable operation IDs become method names in many generators. Confirm that v2 paths do not receive the v1 prefix. ### Add transport policies Configure timeouts, bounded retries, rate-limit handling, idempotency keys for supported writes, and secret management outside generated files. ### Smoke-test one operation per representation Test ordinary JSON, pagination, a mutation, and any special representation your integration consumes. Generated types alone do not prove deployed runtime behavior. ## Coverage checklist Use the generated reference to verify the exact operation rather than inferring coverage from a client namespace. | Surface | Special handling to verify | | --- | --- | | Profiles | Separate `prf_` profile and `sub_` subscription resources; PUT versus PATCH | | Reports | Compact defaults, bounded includes, Markdown/text/audio representations | | News | Topic catalog, JSON or NDJSON pagination, cache-readiness errors | | OSINT | JSON, NDJSON, and GeoJSON; source availability varies | | Intel v2 | Operation-level server override and ontology IDs | | Webhooks | Idempotency, ETags, write-only secrets, and `204` deletion | | Agent Y2 | API-key-only SSE transport, not an ordinary JSON response | | x402 | Payment challenge and settlement headers outside normal bearer auth | --- # Build a Python Client (/docs/sdks/python) This guide builds a dependency-free Python client around Y2's public HTTP contract. Use it directly for small services and jobs, or use the same behaviors as acceptance criteria for a generated client. Load `Y2_API_KEY` from the environment or a secret manager. Notebook output, exception dumps, and committed `.env` files can leak credentials as easily as application logs. ## 1. Configure the environment The examples use Python 3.11 or newer and only the standard library: ```bash export Y2_API_KEY="y2_..." python --version ``` New keys require a Pro or Elite workspace and the scopes needed by the operations you call. ## 2. Create the transport ```python title="y2_client.py" from __future__ import annotations import json import os from dataclasses import dataclass from typing import Any, Mapping, TypeVar, cast from urllib.error import HTTPError from urllib.parse import urlencode from urllib.request import Request, urlopen T = TypeVar("T") API_ORIGIN = "https://api.y2.dev" API_V1 = f"{API_ORIGIN}/api/v1" class Y2ApiError(Exception): def __init__( self, status: int, problem: dict[str, Any] | None, retry_after: str | None, request_id: str | None, ) -> None: detail = problem.get("detail") if problem else None super().__init__(detail or f"Y2 request failed with HTTP {status}") self.status = status self.problem = problem self.retry_after = retry_after self.request_id = request_id @dataclass(frozen=True) class Y2Client: api_key: str timeout_seconds: float = 30.0 @classmethod def from_env(cls) -> Y2Client: api_key = os.environ.get("Y2_API_KEY") if not api_key: raise RuntimeError("Y2_API_KEY is required") return cls(api_key=api_key) def request_json( self, method: str, path_or_url: str, *, query: Mapping[str, str | int | float | bool | None] | None = None, body: Mapping[str, Any] | None = None, headers: Mapping[str, str] | None = None, ) -> T: if path_or_url.startswith("https://"): url = path_or_url elif path_or_url.startswith("/api/"): url = f"{API_ORIGIN}{path_or_url}" else: url = f"{API_V1}{path_or_url}" params = { key: str(value).lower() if isinstance(value, bool) else value for key, value in (query or {}).items() if value is not None } if params: separator = "&" if "?" in url else "?" url = f"{url}{separator}{urlencode(params)}" request_headers = { "Authorization": f"Bearer {self.api_key}", "Accept": "application/json", **(headers or {}), } data = None if body is not None: request_headers.setdefault("Content-Type", "application/json") data = json.dumps(body, separators=(",", ":")).encode("utf-8") request = Request(url, data=data, headers=request_headers, method=method) try: with urlopen(request, timeout=self.timeout_seconds) as response: raw = response.read() if response.status == 204 or not raw: return cast(T, None) return cast(T, json.loads(raw)) except HTTPError as error: raw = error.read() problem = None if raw: try: decoded = json.loads(raw) if isinstance(decoded, dict): problem = decoded except json.JSONDecodeError: pass raise Y2ApiError( error.code, problem, error.headers.get("Retry-After"), error.headers.get("X-Request-Id"), ) from error ``` This helper handles JSON and `204 No Content`. Create separate response readers for Markdown, plain text, NDJSON, GeoJSON, audio redirects, and SSE instead of forcing every media type through `json.loads`. ## 3. Read current News items ```python title="news.py" from typing import Any, TypedDict from y2_client import Y2Client class NewsPage(TypedDict): data: list[dict[str, Any]] meta: dict[str, Any] links: dict[str, str | None] client = Y2Client.from_env() page: NewsPage = client.request_json( "GET", "/news", query={"topics": "markets,macro", "limit": 10}, ) for item in page["data"]: print(item["title"], item["url"]) ``` Use `GET /news/feeds` to discover the current 40-topic catalog. Current items use `title`, `summary`, and a nested `sentiment.label`/`sentiment.value` object—not older top-level `signal` and sentiment scalar fields. ## 4. Create a profile safely ```python title="profiles.py" from typing import Any from y2_client import Y2Client client = Y2Client.from_env() created: dict[str, Any] = client.request_json( "POST", "/profiles", headers={"Idempotency-Key": "supplier-risk-2026-07-21"}, body={ "name": "Critical Supplier Risk", "topic": "Monitor disruption affecting strategic semiconductor suppliers.", "frequency": "daily", "scheduleTimeOfDay": "08:00", }, ) profile_id = created["data"]["id"] print(profile_id) # prf_... ``` Creation returns the profile in `data`. It also creates an active subscription, but retrieve that separate `sub_...` resource from `GET /profiles` when you need to change delivery. Use `PATCH /profiles/{profileId}` for partial changes. `PUT` replaces the complete mutable state, and omitted optional values reset or clear. Retain returned `ETag` headers if your workflow uses `If-Match`; a JSON-only wrapper that discards headers should be extended for that workflow. ## 5. Follow report pagination ```python title="reports.py" from collections.abc import Iterator from typing import Any from y2_client import Y2Client def reports_for_profile( client: Y2Client, profile_id: str, ) -> Iterator[dict[str, Any]]: next_url: str | None = "/reports" first = True while next_url: page: dict[str, Any] = client.request_json( "GET", next_url, query={"profileId": profile_id, "limit": 20} if first else None, ) yield from page["data"] next_url = page["links"]["next"] first = False client = Y2Client.from_env() for report in reports_for_profile(client, "prf_0123456789abcdef01234567"): print(report["id"], report["summary"]) ``` Treat `links.next` and its cursor as opaque. Do not decode, edit, or combine a continuation link with different filters. ## 6. Run independent reads concurrently The standard-library transport is synchronous. For a small job, run independent calls in worker threads without sharing mutable client state: ```python title="concurrent_reads.py" import asyncio from y2_client import Y2Client async def main() -> None: client = Y2Client.from_env() news, feeds = await asyncio.gather( asyncio.to_thread( client.request_json, "GET", "/news?topics=markets&limit=10", ), asyncio.to_thread(client.request_json, "GET", "/news/feeds"), ) print(len(news["data"]), len(feeds["data"])) asyncio.run(main()) ``` For a high-throughput service, replace the transport with an async HTTP client that supports connection pooling, but keep the same authentication, timeout, error, pagination, and media-type rules. ## Handle Problem Details ```python title="errors.py" from y2_client import Y2ApiError, Y2Client client = Y2Client.from_env() try: client.request_json("GET", "/reports/not-a-report-id") except Y2ApiError as error: code = error.problem.get("code") if error.problem else "UNKNOWN" print(error.status, code, error.request_id) if error.status == 429: print("Retry after", error.retry_after) ``` Retry only safe reads or writes protected by an idempotency key. A `404` on tenant-owned resources can intentionally avoid revealing whether another tenant owns the ID. ## Call Intel v2 with the correct base Intel path keys already contain `/api/v2`, and their OpenAPI operation overrides the top-level v1 server: ```python incidents = client.request_json( "GET", "https://api.y2.dev/api/v2/incidents", query={"limit": 20}, ) ``` Do not prepend `https://api.y2.dev/api/v1` to an Intel v2 path. ## Evaluate a generated Python client If you generate models and operation methods from OpenAPI, verify that the result: - honors operation-level server overrides; - represents nullable and optional fields separately; - exposes `application/problem+json` rather than replacing it with generic exceptions; - preserves response headers for rate limits, ETags, request IDs, and x402; - handles `204` and `302` without trying to decode JSON; and - provides explicit streaming APIs for NDJSON and Agent Y2 SSE. A generated package can reduce boilerplate, but its published method names and release cadence are separate from the Y2 API contract. Compare its generated operation IDs with the current endpoint reference before adopting it. --- # Build a TypeScript Client (/docs/sdks/typescript) Use generated OpenAPI types with native `fetch` when you want current schema coverage without depending on release-specific SDK method names. This approach works in Node.js, Bun, serverless functions, and workers with standards-compatible fetch APIs. Do not instantiate a bearer client in browser-delivered code. Put Y2 calls behind your own server, worker, or API route so `Y2_API_KEY` never enters a public bundle. ## 1. Generate declarations Install the same generator used by the Y2 platform repository: ```bash bun add --dev openapi-typescript@^7.13.0 bunx openapi-typescript https://y2.dev/api/openapi.yaml \ --immutable \ --alphabetize \ --output src/y2-api.d.ts ``` ```bash npm install --save-dev openapi-typescript@^7.13.0 npx openapi-typescript https://y2.dev/api/openapi.yaml \ --immutable \ --alphabetize \ --output src/y2-api.d.ts ``` Commit the generated declaration or pin the OpenAPI input in your build pipeline. Regeneration from a moving production URL should be an intentional compatibility update, not an invisible deploy step. Add a repeatable script: ```json title="package.json" { "scripts": { "generate:y2-types": "openapi-typescript https://y2.dev/api/openapi.yaml --immutable --alphabetize --output src/y2-api.d.ts" } } ``` ## 2. Create a JSON transport Set the credential in the runtime environment: ```bash export Y2_API_KEY="y2_..." ``` Then create a transport that preserves Y2 Problem Details and rate-limit context: ```typescript title="src/y2-client.ts" import type { components } from "./y2-api"; const API_ORIGIN = "https://api.y2.dev"; const API_V1 = `${API_ORIGIN}/api/v1`; export type Y2Problem = components["schemas"]["ErrorResponse"]; export class Y2ApiError extends Error { constructor( readonly status: number, readonly problem: Y2Problem | null, readonly retryAfter: string | null, readonly requestId: string | null, ) { super(problem?.detail ?? `Y2 request failed with HTTP ${status}`); } } function isProblem(value: unknown): value is Y2Problem { return ( typeof value === "object" && value !== null && "status" in value && "code" in value && "detail" in value ); } function resolveUrl(pathOrUrl: string): string { if (pathOrUrl.startsWith("https://")) return pathOrUrl; if (pathOrUrl.startsWith("/api/")) return `${API_ORIGIN}${pathOrUrl}`; return `${API_V1}${pathOrUrl}`; } export async function y2Json( pathOrUrl: string, init: RequestInit = {}, ): Promise { const apiKey = process.env.Y2_API_KEY; if (!apiKey) throw new Error("Y2_API_KEY is required"); const headers = new Headers(init.headers); headers.set("Authorization", `Bearer ${apiKey}`); if (!headers.has("Accept")) headers.set("Accept", "application/json"); if (init.body && !headers.has("Content-Type")) { headers.set("Content-Type", "application/json"); } const response = await fetch(resolveUrl(pathOrUrl), { ...init, headers, signal: init.signal ?? AbortSignal.timeout(30_000), }); const text = await response.text(); let payload: unknown = null; if (text) { try { payload = JSON.parse(text); } catch { payload = text; } } if (!response.ok) { throw new Y2ApiError( response.status, isProblem(payload) ? payload : null, response.headers.get("Retry-After"), response.headers.get("X-Request-Id"), ); } return payload as T; } ``` `y2Json` is intentionally JSON-only. Use separate functions for NDJSON, Markdown, plain text, audio redirects, and Agent Y2 SSE so a content-type mismatch cannot be silently cast to a JSON model. ## 3. Type a read operation Extract parameter and response types from the path entry: ```typescript title="src/news.ts" import type { paths } from "./y2-api"; import { y2Json } from "./y2-client"; type ListNewsQuery = NonNullable< paths["/news"]["get"]["parameters"]["query"] >; type ListNewsResponse = paths["/news"]["get"]["responses"][200]["content"]["application/json"]; export async function listNews(query: ListNewsQuery): Promise { const params = new URLSearchParams(); if (query.topics) params.set("topics", query.topics); if (query.limit !== undefined) params.set("limit", String(query.limit)); if (query.cursor) params.set("cursor", query.cursor); if (query.format) params.set("format", query.format); return y2Json(`/news?${params}`); } const page = await listNews({ topics: "markets,macro", limit: 10 }); for (const item of page.data) { console.log(item.title, item.url); } ``` Use `GET /news/feeds` to discover the current 40-topic catalog. Current items use `title`, `summary`, and a nested `sentiment.label`/`sentiment.value` object—not older top-level `signal` and sentiment scalar fields. ## 4. Type a mutation Profile creation returns a `PublicProfile` directly in `data`. Its ID is `data.id`; profile creation also creates a subscription, but that `sub_...` ID is obtained from `GET /profiles`. ```typescript title="src/profiles.ts" import type { paths } from "./y2-api"; import { y2Json } from "./y2-client"; type CreateProfileBody = paths["/profiles"]["post"]["requestBody"]["content"]["application/json"]; type CreateProfileResponse = paths["/profiles"]["post"]["responses"][201]["content"]["application/json"]; export async function createProfile( body: CreateProfileBody, idempotencyKey: string, ): Promise { return y2Json("/profiles", { method: "POST", headers: { "Idempotency-Key": idempotencyKey }, body: JSON.stringify(body), }); } const created = await createProfile( { name: "Critical Supplier Risk", topic: "Monitor disruption affecting strategic semiconductor suppliers.", frequency: "daily", scheduleTimeOfDay: "08:00", }, "supplier-risk-2026-07-21", ); console.log(created.data.id); // prf_... ``` Use `PATCH /profiles/{profileId}` for partial changes and `PUT` only for full replacement. Preserve the `ETag` returned by create or update when you need `If-Match` concurrency protection. ## 5. Paginate reports Follow the response link instead of decoding or modifying an opaque cursor: ```typescript title="src/reports.ts" import type { paths } from "./y2-api"; import { y2Json } from "./y2-client"; type ReportsPage = paths["/reports"]["get"]["responses"][200]["content"]["application/json"]; export async function* reportsForProfile(profileId: string) { let next: string | null = `/reports?profileId=${encodeURIComponent(profileId)}&limit=20`; while (next) { const page: ReportsPage = await y2Json(next); yield* page.data; next = page.links.next; } } for await (const report of reportsForProfile("prf_0123456789abcdef01234567")) { console.log(report.id, report.summary); } ``` The list and default detail response are compact. Request `include=content,sources` or `Accept: text/markdown` only when the consumer needs those representations. ## Call Intel v2 correctly The OpenAPI path key includes the full `/api/v2` prefix, and the operation overrides the document server with `https://api.y2.dev`: ```typescript type IncidentList = paths["/api/v2/incidents"]["get"]["responses"][200]["content"]["application/json"]; const incidents = await y2Json( "https://api.y2.dev/api/v2/incidents?limit=20", ); ``` Do not compose Intel URLs by adding `/api/v1` to the path. ## Handle special representations Request `application/x-ndjson`, read `response.body` as a stream, split complete newline-delimited records, and retain `X-Y2-Next-Cursor`. Do not call `response.json()` on the whole stream. Send `Accept: text/markdown` to `GET /reports/{reportId}` and use `response.text()`. The response is the report body, not a `{ data }` envelope. Agent Y2 returns `text/event-stream` in the Vercel AI SDK data-stream format. Use an SSE-aware streaming consumer and retain `X-Thread-Id`; do not route it through `y2Json`. ## Production checklist - Pin or review generated declaration changes. - Give the key only the required scopes. - Use idempotency keys on supported create operations. - Follow `Retry-After` on `429` and retry only safe or idempotent requests. - Log `X-Request-Id` without logging credentials or full payment payloads. - Treat `404` as non-disclosing for tenant-owned resources. - Test special media types separately from JSON. --- # Vision (/docs/vision) Y2's vision is an intelligence workspace where every decision-maker can maintain relevant context, understand what changed, and act without rebuilding the research process from scratch. This page explains the design direction of Y2. For current product availability, use [Plans and limits](/docs/plans) and the [Changelog](/docs/changelog). ## The intelligence loop ```mermaid flowchart LR sources["Open sources and operator inputs"] --> context["Connected intelligence context"] context --> research["Human and agent research"] research --> outputs["Briefings, alerts, graphs, and APIs"] outputs --> decisions["Operator decisions"] decisions -. "new priorities" .-> context ``` The goal is not a larger stream of information. It is a durable loop in which new evidence updates shared context, research produces an inspectable result, and operator priorities shape the next cycle. ## Design direction ### Persistent rather than one-off A question that matters today will often matter again tomorrow. Profiles and saved workspace context should make recurring monitoring easier than restarting the same search. ### Connected rather than isolated Reports, events, entities, sources, and investigations should reinforce one another. Readers should be able to move from a summary to the evidence, relationships, and prior context behind it. ### Human-directed agents AI should extend an analyst's reach while preserving user control. The operator defines the goal, reviews the evidence, and decides what action—if any—to take. ### Usable wherever decisions happen Intelligence should work in the Y2 interface and through delivery channels, APIs, and interoperable contracts. A useful result should not be trapped in one screen. ### Compounding community knowledge Reusable profiles and shared intelligence patterns can reduce duplicated work. Community knowledge becomes more valuable when its authorship, scope, and source context remain understandable. ## What remains constant - Operators control the topics, workspace, and delivery paths that matter to them. - Source context and limitations remain visible enough to support independent judgment. - Product capabilities are documented as they exist, separately from long-term direction. - Integrations use explicit contracts so teams can build without depending on hidden behavior. ## Continue exploring Understand the access and decision-support problem Y2 is designed to address. Compare available workspace features and enforced limits. Follow shipped improvements and current release notes. --- # InfoOps Report Workflow (/docs/workflows) Every active InfoOps profile runs through a fixed, durable report workflow. The workflow searches the web, optionally expands the topic into child searches, synthesizes one report, enriches it, delivers it to subscribers, and schedules the next run. This page documents the built-in report pipeline configured through an InfoOps profile. Pro and Elite users can also create [Automations](/docs/automations) that run bounded Agent Y2 work on a schedule or after a profile report completes. ```mermaid flowchart LR A[Profile trigger] --> B[Root web research] B --> C{Recursive layer?} C -->|Enabled| D[2-3 parallel child searches] C -->|Disabled| E[Final synthesis] D --> E E --> F[Optional audio] F --> G[Required enrichment] G --> H[Subscriber delivery] H --> I[Update state and schedule next run] ``` ## How a run starts A report run is attached to a profile, not created as an independent workflow object. Y2 queues a run when: - An active custom profile is created. - A paused or cancelled profile becomes active. - An active profile's status or schedule changes. - Its next scheduled execution time arrives. - Recovery logic restarts a failed or stale run. The start guard skips inactive profiles, prevents a second live run for the same profile, and defers new starts while a retry is already scheduled. Creating an active profile therefore starts an initial report immediately and also schedules its next normal occurrence. The workflow engine runs for active profiles on every plan. Plan-specific profile, delivery, audio, API, and workspace limits still apply to the surrounding features. ## Research and synthesis ### 1. Search the root topic Y2 builds a search query from the profile topic and topic-specific hints, then calls Tavily. The research action honors these saved search fields: The search action validates freshness using the engine's default topic-aware rules. When it finds stale sources and still has more than three results, it removes stale URLs before synthesis. The profile's **Source Freshness** and **Link Validation** values are stored, but the current research action uses its centralized default freshness configuration rather than those saved switches. Domain and time-range controls above are applied directly. ### 2. Expand one child layer When recursion is enabled, an AI step proposes two or three concise, search-ready subtopics. Y2 researches those child topics in parallel and merges successful findings and unique source URLs with the root results. A failed child does not discard successful root or sibling research. The engine is breadth-first and caps effective recursion at one child layer. It uses a 480-second overall research budget and reserves up to 120 seconds for the child batch before final synthesis. The current profile editor offers 1-, 2-, and 3-layer labels, but it always saves recursion as enabled. At runtime, an enabled depth of `0` defaults to `1`, and values above `1` are capped at `1`. The three app choices therefore execute at most one child layer. A public API profile with `recursionConfig.enabled: false` is the current way to request a true root-only run. The saved `strategy` value is accepted by profile schemas, but the current engine always runs the implemented breadth-first path. ### 3. Synthesize one report Only the root layer performs synthesis. Y2 combines root and child findings, deduplicates sources, and truncates the combined research document at 35,000 characters before generating one report. The synthesis prompt includes: - The profile topic and up to 2,000 characters of custom instructions. - The saved report format, including supporting instructions, or the default BLUF structure. - Shared OSINT writing guidance for sourced judgments, confidence, alternatives, and evidence gaps; user formatting instructions take precedence over its presentation defaults. - Resolved branding terminology. - A compact summary of up to three previous reports for continuity. - A structured SIGINT contract and the discovered source URLs. Y2 rejects incomplete synthesis that lacks H2 sections, ends abruptly, or contains no usable signals. A failed quality check stops the workflow before audio or delivery rather than sending a partial report. ## Models and provider routing The app does not expose a report-model selector. The current subtopic and synthesis cascades are fixed in the workflow code: | Stage | Primary | Fallback order | | --- | --- | --- | | Subtopic identification | `z-ai/glm-5.3-flash` | `google/gemini-3.1-flash-lite` | | Final synthesis | `z-ai/glm-5.3-flash` | `minimax/minimax-m2.7`, then `google/gemini-3.1-flash-lite` | On a network failure, a model can be retried once. Parse, timeout, context, or structured-output failures can advance to the next distinct model. If subtopic generation exhausts retryable model attempts, Y2 can use three deterministic child-query templates; final synthesis has no equivalent partial-report fallback. Public profile schemas currently accept `modelConfig.modelId`, but the executed subtopic and synthesis model order comes from the fixed cascades above. Temperature and maximum output-token overrides are read; a custom model ID does not replace the cascade's primary model. Provider requests deny data collection through the configured OpenRouter routing options. See the platform's privacy and data-processing terms for the complete data-handling boundary. ## Post-processing and delivery After synthesis, the durable workflow runs these steps in order: ### Generate optional audio If the profile has Audio Narration enabled, Y2 attempts to create and store the MP3. Audio is non-critical; a failed audio step does not block the report. ### Enrich the report Y2 extracts report events and builds the ontology graph and incident metadata used by downstream product surfaces. Enrichment is required and must finish before delivery. ### Deliver to subscribers Y2 fans the report out according to each active subscription's email, SMS, combined, or webhook configuration. Delivery is critical; a terminal delivery failure fails the run. ### Finalize and reschedule Y2 updates the profile's last-delivery timestamp and hands off the next normal schedule. Recovery logic repairs the schedule when that final handoff fails. The compact webhook event contains report state, signal and graph counts, audio availability, and links to richer API representations. It does not embed the full report body. See [Webhook Delivery](/docs/delivery/webhooks) for the current contract. ## Failure recovery Y2 records report-generation state on the profile as `generating`, `completed`, `failed`, or `canceled`. A run receives a 45-minute lease. The recovery watchdog can renew an in-progress lease once for 30 minutes before treating an unresolved run as stale. Terminal failures can be retried up to three times with delays of 5 minutes, 15 minutes, and 1 hour. When workflow history is available, recovery restarts from the last failed durable step; otherwise it queues a fresh run. After retries are exhausted, Y2 returns to repairing the normal profile schedule instead of retrying indefinitely. Duplicate starts are skipped while a valid generation lease or future retry exists. If a newly activated profile does not immediately produce a second report, an existing run may already be active or waiting for recovery. ## Configure the implemented workflow ### Define the research target In **InfoOps → My Profiles**, create or edit a profile. Use a specific topic and custom instructions that describe the desired decisions, geography, entities, and output emphasis. ### Choose report structure and depth Set the BLUF sections and review the recursion limitation above. Use the public Profiles API with recursion disabled when a root-only run is required. ### Set search boundaries In **Advanced Configuration**, choose search depth, time range, result count, and optional include or exclude domains. ### Configure delivery and optional outputs Set the schedule and subscriber method, then enable audio or branding only when the active workspace has those entitlements. ### Activate and review the first report Saving a new active profile queues an immediate run. Review its sources, signals, graph, and delivery history before relying on later scheduled reports. ## API boundary There is no public endpoint for creating arbitrary workflows or directly starting an internal workflow ID. Use the Profiles API to create, update, activate, pause, or cancel the configuration; use the Reports API to read produced reports and derived representations. The generated [Profiles reference](/docs/api/reference/profiles) is the canonical request schema. Profile writes require `profiles:write`; report reads require the relevant `reports:*` scope. ## Next steps Configure the inputs that feed the durable report pipeline. Understand UTC scheduling, jitter, and schedule recovery. Configure the optional narration step. Consume the compact report-generated event safely. --- # Workspaces (/docs/workspaces) A workspace holds its own plan, resources, API keys, usage, and memberships. All workspaces use the same model. Free, Lite, and Pro include one seat; Elite includes five seats, including the owner. Your first workspace is created during onboarding. Retrying setup or checkout resumes that workspace. An invited teammate joins the invited workspace without creating another one. ## Upgrade your workspace Open **Settings → Billing** to manage the active workspace's plan. Upgrading from Pro to Elite unlocks team seats in that same workspace. Its profiles, reports, keys, projects, and billing history stay with it. Only the owner can manage billing. An Elite workspace can have an approved higher seat allowance. Contact Y2 to arrange expansion. Additional seats do not multiply the workspace's credits, profiles, Automations, or API limits. ## Switch or create workspaces Use the workspace menu in the app sidebar to switch. Each entry shows its name, plan, and your role. **Settings → Workspace** also shows seat usage and lets owners and admins rename it. To create a separate workspace, select **New workspace** and enter a name of 2–80 characters. It starts on Free with its own resources and billing. Upgrade that workspace to Elite to invite teammates. Creating it does not copy another workspace's subscription or data. Switching changes which workspace the app reads and writes. It does not move resources. Suspended memberships do not appear in the switcher. If no accessible workspace remains, the app lets you create one before continuing. ## Invite teammates on Elite 1. Switch to the workspace and open **Settings → Members**. 2. An owner or admin creates an invitation for an email address and an `admin`, `member`, or `viewer` role. 3. The recipient signs in with the matching verified email and requests access. 4. A workspace owner or admin approves the request. The recipient can then use that workspace. The owner, active members, and unexpired pending invitations must fit within the seat allowance. An invitation reserves a seat; suspended members do not. Owners and admins can revoke invitations. Owners can change non-owner roles or remove members. A non-owner can leave the workspace. ## Roles and management permissions | Operation | Owner | Admin | Member | Viewer | | --- | :---: | :---: | :---: | :---: | | Switch into an active seat | Yes | Yes | Yes | Yes | | View the member roster | Yes | Yes | Yes | Yes | | Rename the workspace | Yes | Yes | No | No | | Invite people, approve requests, revoke invites | Yes | Yes | No | No | | View governance and audit activity | Yes | Yes | No | No | | Manage workspace API keys | Yes | Yes | No | No | | Change another member's role or remove them | Yes | No | No | No | | Manage billing | Yes | No | No | No | | Delete an empty workspace | Yes | Yes | No | No | Role requirements also depend on the resource. Owners and admins can manage workspace profiles; members can write profiles they created; viewers cannot write profiles. Projects and Automation definitions remain private to their owner. Governance access does not grant access to another member's private Project content. ## Seat limits and downgrades When a workspace moves to a one-seat plan, the owner keeps access and other memberships are suspended. Their membership records remain available for restoration. Returning to Elite restores members in a deterministic order up to the available seat allowance. A downgrade applies only to that workspace. Moving to Free or Lite pauses profiles beyond the new allowance while preserving their ownership and privacy. Resources in other workspaces are unaffected. ## Governance and shared limits On Elite, owners and admins can open **Settings → Governance** to review the shared Agent Y2 credit pool, usage by member, API keys, and recent audit records. Credit attribution does not create separate allowances for each member. Activity units are informational and are not Agent Y2 credits. The audit log includes membership actions and material Project and Automation changes. It is not a complete log of every report, chat message, model call, or API operation. Pro and Elite support workspace API keys. Owners and admins manage them; requests use the workspace associated with the key. Rotating keys or adding members does not multiply request limits or credit allowances. See [Plans and limits](/docs/plans) for current allowances. ## Delete a workspace An owner or admin can delete an empty workspace from **Settings → Workspace** by typing its name. The checklist identifies billing subscriptions and dependent resources that must be resolved first. Deletion removes the workspace and its memberships, invitations, audit records, and rate counters. Users with another accessible membership can continue there. Account deletion also checks workspace ownership. An account that owns a workspace with other members must resolve that shared ownership before its billing or account can be removed. ## Related guides Compare workspace features, seats, API keys, and usage limits Create and protect workspace credentials Understand profile ownership and workspace capacity