# Join and Manage the Affiliate Program (/docs/affiliate-program) Use [**Settings → Referrals**](/app/settings/referrals) to join the Y2 Affiliate Program, copy your personal link, monitor referral activity, and check payout eligibility. This guide is for paid Y2 customers who own the billing workspace attached to their subscription. Enroll, copy your link, refresh activity, and review payout eligibility Compare the Lite, Pro, and Elite plans that qualify a promoter account Participation is optional. Y2 creates a promoter link only after you accept the current program terms. The version shown in Settings controls if it differs from this operational summary. ## Before you begin Your account must meet all three enrollment requirements: | Requirement | What qualifies | | --- | --- | | Verified email | The email in [Account settings](/app/settings/account) has been verified | | Owned paid workspace | Your account owns a Lite, Pro, or Elite billing workspace | | Current payment | The owned workspace has an active Stripe subscription that is not expired or canceled | Admin or member access to another customer's paid organization does not qualify. If billing needs attention, open [Billing settings](/app/settings/billing) before enrolling. ## Join and create your link ### Open referral settings Go to [**Settings → Referrals**](/app/settings/referrals). The program card shows whether you need to verify your email, restore billing, accept the terms, or wait for link activation. ### Review the current terms Expand **Review the affiliate program terms**. Check the commission rules, review period, payout requirements, and responsible-promotion rules for the displayed version. ### Accept and join Select the agreement checkbox, then choose **Join and create my referral link**. Y2 records the terms version and commission percentage you accepted before starting enrollment. ### Copy the activated link Link creation runs asynchronously through FirstPromoter. When the complete URL appears, choose **Copy link** and use that URL in your promotion. If activation is still pending, wait briefly and use **Refresh referral activity**. ## What happens after enrollment ```mermaid flowchart LR enroll["Accept current terms"] --> provision["FirstPromoter link provisioned"] provision --> visit["New customer opens link"] visit --> claim["Attribution claimed at workspace creation"] claim --> payment["Non-zero Lite or Pro payment"] payment --> review["Commission pending review"] review --> payout["Approved balance paid when eligible"] ``` A signup, trial, or workspace creation is not a commissionable conversion. Y2 records an eligible sale only after Stripe confirms qualifying collected revenue. ### Your referral link Y2 asks FirstPromoter to base a new link token on your lowercase Y2 username. If that token is already in use, provisioning can append a six-character suffix. Once an established promoter link is stored, later synchronization keeps it stable rather than renaming it whenever your account is read. Always copy the link shown in the referral dashboard. Do not reconstruct or shorten its `via` token yourself. ### Read the dashboard After enrollment, the page replaces the join form with your activity dashboard. | Dashboard area | What it tells you | | --- | --- | | Clicks | Provider-reported visits through your referral link | | Referrals | Referred-customer records attributed to your promoter account | | Sales | Provider-reported qualifying sales | | Active customers | Referred customers currently counted as active | | Payout eligibility | Whether the link, terms, owned plan, and payment requirements are all current | | Commission activity | Recent amount, currency, review state, and paid state | | Referral activity | Recent referred-customer state and customer-since date when available | | Last synced | When Y2 last refreshed activity from FirstPromoter | The dashboard synchronizes these provider values and shows masked customer email addresses when available. Use **Refresh referral activity** beside the link to request a new synchronization. ## Preserve attribution For the strongest chance of correct attribution, the referred customer should: 1. Open the complete referral link before creating a Y2 workspace. 2. Allow marketing/referral tracking in the privacy choices. 3. Complete account and default-workspace setup in the same browser. 4. Purchase an eligible Lite or Pro subscription before attribution expires. The browser starts FirstPromoter and captures referral attribution only when marketing consent is enabled. Declining that category, changing browsers, clearing site storage, or removing the link parameters can prevent the referral from being claimed. Y2 keeps the first current referral touch it captures in browser storage. When the new customer creates their default workspace, Y2 claims that attribution for the workspace and sets a 90-day expiration. A workspace can have only one attribution record; later links do not replace it. If the attribution is still only **claimed** when it expires, a later payment is rejected for affiliate tracking. Once a qualifying first payment converts it, later eligible invoices can be associated with the same attributed subscription. ## What earns commission The default program offers **15% recurring commission** on eligible net Lite or Pro subscription revenue actually collected by Y2. | Commissionable | Not commissionable | | --- | --- | | Legitimate new customer attributed through the assigned link | Existing customer, duplicate account, or self-referral | | Successful, non-zero Lite or Pro payment | Signup, workspace creation, or trial without collected revenue | | Tax-exclusive cash collected after credits and discounts | Free, Elite, zero-dollar, fully discounted, or test activity | | Revenue that passes payment, attribution, fraud, and eligibility review | Refunds, chargebacks, disputes, uncollected amounts, or manipulated attribution | The collected amount is capped at the invoice's tax-exclusive total. Taxes are not part of the commission basis. Recurring commission continues only while the attributed Lite or Pro subscription remains active and paid, the underlying revenue remains eligible, and your own promoter eligibility remains current. ## Keep your promoter eligibility current The dashboard evaluates four requirements: - Referral link activated in the accepted FirstPromoter campaign. - Current Y2 affiliate terms accepted. - At least one Lite, Pro, or Elite billing workspace owned by your account. - An active, current Stripe subscription for an owned eligible workspace. If the owned billing account becomes Free, trialing, incomplete, canceled, past due, unpaid, or delinquent, commission and payout eligibility pauses. Access through someone else's organization does not restore it. ## Review and payout Commissions may remain pending for at least **60 days** while Y2 reviews payment finality, refunds, disputes, fraud, attribution, plan eligibility, and promoter eligibility. A pending amount is an estimate, not an approved payable balance. Y2 may deny, reverse, offset, or recover an amount that becomes ineligible. The current in-app terms state a default payout threshold of **$100**. Identity, tax, invoice, or payout-method details may be required before payment, and the promoter is responsible for taxes on payouts received. ## Troubleshooting Verify the account email in **Settings → Account**, then reopen **Settings → Referrals**. Y2 cannot create a promoter link until verification is complete. Confirm that your account owns the billing workspace. Restore an active Lite, Pro, or Elite Stripe subscription on an owned workspace; membership in another paid organization does not qualify. Enrollment is asynchronous. Wait for the provider sync, then use **Refresh referral activity**. Temporary rate limits, timeouts, and provider outages retry automatically. If the dashboard displays **Retry**, choose it once the provider is available again. Follow the message shown on the dashboard. If it asks for support review, contact [support@y2.dev](mailto:support@y2.dev) and include the error text. Do not create a duplicate account to work around an email or promoter ownership conflict. Confirm that the customer used the complete link before workspace creation, allowed marketing tracking, stayed in the same browser, was new to Y2, and paid a non-zero Lite or Pro invoice within the attribution window. Signups, trials, Elite purchases, self-referrals, and duplicate accounts do not qualify. Describe Y2 truthfully and disclose your material connection near each promotion. Do not impersonate Y2, spam, stuff cookies, bid on Y2 trademarks, or offer unapproved incentives. --- # 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, chat credit budget, persisted thread history, and allowed Y2 tools. API callers cannot select an arbitrary model, replace the fixed system instructions, enable onboarding mode, or attach files in v1. The `model` value on the OpenAI-compatible route is a Y2 routing alias, not model selection. ## 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 chat-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 `402` response from these routes describes plan, chat-credit, or upstream-provider credit exhaustion; 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-delta chunks, a final chunk with `finish_reason: "stop"`, 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. System, developer, and tool messages are accepted so OpenAI-oriented clients can serialize their transcript, but they are excluded from Agent Y2's model context. Extra OpenAI fields such as `temperature`, `max_tokens`, and tool definitions are not part of the v1 contract and do not configure the agent. ## 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 | Accepted where compatible but not used to override or extend the fixed prompt | 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. 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`. ## Error guide | Status | Common cause | Response | | --- | --- | --- | | `400` | Invalid JSON, missing latest user text, unsupported alias, 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, monthly chat-credit, or upstream provider credit exhaustion | Check plan and chat usage; 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 ```mermaid flowchart LR discover["Discover\nllms.txt"] --> context["Load only relevant docs"] context --> contract["Read OpenAPI operation"] contract --> access["Choose tenant + scopes"] access --> implement["Implement server-side"] implement --> verify["Smoke test + inspect headers"] ``` ### 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 | 5 | 30/minute and 5,000/day | | Elite | 25 | 120/minute and 50,000/day | Elite organization workspaces can include up to five seats. 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 | 5 | 30/minute and 5,000/day | New API-key access | | Elite | 25 | 120/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 | 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:*`, or `automations:*`. `profiles:write`, `projects:write`, `automations: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 a compatibility `error` object: ```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_...", "error": { "message": "Insufficient permissions. Requires reports:read scope", "code": "INSUFFICIENT_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 Chat 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, and report graph context 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. ## 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. ## 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 | 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 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 /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. Every current v2 operation except Y2 Global Knowledge also declares x402 metadata in OpenAPI. Global Knowledge is API-key-only because its authorization requires a Y2 workspace principal. ## 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 a v1 compatibility object: ```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_...", "error": { "message": "Invalid API key", "code": "INVALID_API_KEY" } } ``` | 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.1` | | `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. ```mermaid flowchart LR A{What are you building?} A -->|Grounded answer| B[Report metadata + Markdown] A -->|Map or spatial analysis| C[GeoJSON FeatureCollection] A -->|Analytics warehouse| D[Intel v2 JSON pages] A -->|Continuously updated system| E[Change feed + 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. ```mermaid flowchart LR A[List a focused collection] --> B[Persist public IDs] B --> C[Drill into an incident or entity] C --> D[Include bounded related resources] C --> E[Traverse an entity graph] ``` 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 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`, and `observations`. 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`. ## Select the correct scope | Scope | Accepted operations | | --- | --- | | `intel:explorer` | Entities, entity details and graphs, unfiltered incidents, and general signals | | `intel:finint` | Markets, FININT indicators, 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 Every Intel endpoint accepts a scoped bearer API key or its documented [x402 payment flow](/docs/api/x402). 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/mcp` connects MCP-compatible clients to Y2 documentation, the OpenAPI contract, bounded API reads, and Agent Y2. The documented release runs locally over stdio and calls Y2 over HTTPS. ```mermaid flowchart LR A[MCP client] <-->|stdio| B[@y2-intel/mcp] B -->|HTTPS| C[Y2 API] B -->|HTTPS| D[Y2 docs and OpenAPI] ``` The MCP server is a separate public package; its implementation is not in the platform repository. This page documents the intended integration contract. After installation, treat MCP `tools/list`, `resources/list`, and `prompts/list` as the source of truth for that installed package version. ## Before you begin You need: - Node.js with `npx` - An MCP client that supports local stdio servers - A Y2 API key with only the scopes the client needs Create a separate key for each MCP client so it can be revoked and audited independently. Keep `Y2_API_KEY` in the client environment or its protected configuration; never put it in a prompt or tool argument. ## 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 command syntax above matches the locally installed Codex CLI. The `--env` value is saved with the MCP server configuration, so 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 package behavior The documented package accepts these environment variables: Override base URLs only for a Y2-controlled development or test environment. Do not point a credentialed MCP process at an untrusted proxy. ## Verify the installed capabilities Run the MCP Inspector before allowing an agent to call tools: ```bash npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \ --method tools/list npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \ --method resources/list npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \ --method prompts/list ``` The documented release is expected to expose: | Capability | Name | Y2 authorization | | --- | --- | --- | | Resource | `y2://docs/index` | None | | Resource | `y2://docs/full` | None | | Resource | `y2://openapi` | None | | Resource | `y2://quickstart` | None | | Tool | `y2_get_openapi_operation` | None | | Tool | `y2_list_reports` | `reports:read` | | Tool | `y2_get_report` | `reports:read` | | Tool | `y2_list_news` | `news:read` | | Tool | `y2_ask_agent` | `agent:y2` | It also documents the prompts `integrate-y2-api`, `ask-y2-brief`, and `debug-y2-api-call`. If inspection returns a different inventory, follow the schemas returned by the installed server rather than guessing arguments from this list. Test a no-key operation first: ```bash npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \ --method tools/call \ --tool-name y2_get_openapi_operation \ --tool-arg operationId=listReports ``` Then create a scoped key and test one bounded read such as `y2_list_reports`. Enable `y2_ask_agent` only after read-only tools work as expected. `agent:y2` allows the fixed Y2 agent to use tools available under the key's scopes and plan. Those tools can change account resources. Omit this scope unless the MCP client is intended to perform those actions. ## Troubleshoot startup | 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` | Add the required scope to a new key; key scopes are fixed at creation | | Tool returns `429` | Respect `Retry-After` and the rate-limit headers | | Agent tool returns a credit error | Check Agent Y2 chat credits 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. ```mermaid flowchart LR A[Legacy reader] --> B[Read additive compatibility response] B --> C[Persist public IDs and semantic time] C --> D[Follow canonical pagination and errors] D --> E[Remove legacy field reads] ``` 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`, and `requestId`. A deprecated nested `error` object remains for v1 compatibility. ```ts if (!response.ok) { const problem = await response.json(); logger.error({ code: problem.code, status: problem.status, requestId: problem.requestId, }); 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. ```mermaid flowchart LR A[Discover feed IDs] --> B{Required output} B -->|Individual items| C[Page /news] B -->|Topic synthesis| D[Read /news/recaps] C --> E[Join to OSINT or reports] D --> E ``` 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. --- # 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. [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. ```mermaid flowchart LR A{Question} -->|What happened?| B[Events or regional search] A -->|Where?| C[Map or GeoJSON] A -->|Country context?| D[Country resources] A -->|Provider healthy?| E[Source status] A -->|Entity relationships?| F[Intel v2] ``` 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 country events represented as news | `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`. ## 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 --- # 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} ); } --- # 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 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 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} ); } --- # 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} ); } --- # 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 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. ```mermaid flowchart LR A[List reports] --> B[Persist rpt_ ID] B --> C{Consumer} C -->|Agent| D[Markdown + cited sources] C -->|Application| E[Compact or expanded JSON] C -->|Analytics| F[Signals or graph] C -->|Human| G[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 five 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. Production delivery times out after 10 seconds. Y2 does not run an independent HTTP retry loop for a failed attempt, although a replayed delivery workflow can attempt the same persisted logical event again. Five consecutive failed attempts automatically disable the webhook; a successful attempt resets the failure counter. `GET /webhooks` exposes `status`, `deliveryHealth.consecutiveFailures`, and `deliveryHealth.lastUsedAt`. To re-enable a destination, replace its complete configuration with `PUT` and set `isActive` to `true`, then test it again. ## Replace or delete a configuration `PUT /webhooks/{webhookId}` replaces the complete mutable configuration. `name` and `url` are required, and omitted optional fields reset to defaults. 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. ```mermaid flowchart LR A[Open endpoint category] --> B[Choose an operation] B --> C[Set path and query values] C --> D[Run with pasted key] C --> E[Copy secret-safe snippet] D --> F[Inspect status and body] E --> G[Move into your codebase] ``` ## 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 user or workspace context encoded by the pasted key. An Elite organization key operates on organization resources, not a member's separate personal 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. --- # 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. ```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] ``` ## 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. 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 25. 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 | 25 | | 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 50. The limit is shared by the 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] ``` ## 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. ## 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 | 50 | 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`, `refunded`, and `none`. 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) [← 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 Y2 Elite is live as the flat-priced B2B organization plan. Elite is priced at **$500/mo flat** or **$5,000/yr** and includes 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 | |---------|-------| | Price | $500/mo flat or $5,000/yr | | 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) [← 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 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). --- # Platform Changelog (/docs/changelog) Follow product changes across intelligence coverage, Chat, API access, and platform operations. The first entry describes the current release. Monthly archives preserve the behavior announced at that time; linked feature and API guides describe the platform as it works today. ## Current release: August 7, 2026 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). ## Previous releases Earlier release notes are grouped into monthly archives. Each link opens the full release entry. ### August 2026 - [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 a separate feature-credit reservation and ledger. A zero-cost internal tool can still be part of a metered chat response. Model input and output tokens consume chat 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. ## Chat credits One credit represents $0.01 of normalized usage. 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 | Base chat allocation | Chat access | | --- | ---: | --- | | Free | 0 credits | No | | Lite | 100 credits per month | Yes | | Pro | 1,000 credits per month | Yes | | Elite | 5,000 credits per month | Yes, with workspace governance views | The usage meter reports credits used, limit, remaining credits, percentage, message count, input tokens, output tokens, period, and last update time. ### Billing periods Paid workspaces use the active subscription's billing boundaries. A monthly subscription receives the base monthly allocation for that period. A yearly subscription receives 12 times the monthly allocation for its yearly period. If no active paid-subscription period is available, usage falls back to the calendar month. At 80% usage the UI displays a warning, at 95% a critical warning, and at 100% it disables new messages until the next billing period. Some interface labels say “monthly” even for annual subscriptions. The backend limit and reset boundary follow the actual subscription interval. ## 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 credit meter. At 100% of the current billing-period allocation, 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 Y2 chat 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 | ```mermaid flowchart TD idea["Contribution"] --> content{"What changes?"} content -->|Public briefing| share["Publish from profile settings"] content -->|Product or docs| feedback["PostHog Support or support email"] content -->|Security issue| private["Private support email"] content -->|SDK or CLI code| repository["Tool-specific repository process"] content -->|Integration| contract["OpenAPI + webhooks"] ``` ## 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 ```mermaid flowchart LR author["Profile owner"] toggle["Share with Community"] catalog["Discover catalog
Global + community profiles"] follow["Follow in active workspace"] reports["My Briefings
Reports + delivery settings"] author --> toggle --> catalog --> follow --> reports ``` 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 custom profiles and subscriptions | | Elite | Up to 50 total profiles, including custom profiles and 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 50-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. ```mermaid flowchart LR catalog["Public catalog"] --> group["Browse group"] group --> search["Search + filters"] search --> details["Inspect profile details"] details --> follow["Follow in active workspace"] follow --> briefings["My Briefings"] ``` ## 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. ```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] ``` ## 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. ## 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 five 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); }); ``` Y2 does not run an independent automatic webhook retry loop. A timeout, network error, or non-2xx response increments the configuration's consecutive failure count. A successful delivery resets the count; five consecutive failures disable the webhook. ## Manage configurations - Editing a masked webhook leaves its existing secret unchanged when the secret field is blank. - Activating a webhook 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 ```mermaid flowchart LR topic["Choose a topic"] --> profile["Create or subscribe to a profile"] profile --> research["Y2 researches and synthesizes"] research --> report["Receive or open the report"] ``` 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. 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. ```mermaid flowchart LR A[Configured RSS and X sources] --> B[Scheduled ingestion] B --> C[Topic caches] C --> D[Live Feed] C --> E[12h Recap] D --> F[Source link] ``` “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. ## 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. --- # Country Intelligence Cards (/docs/osint/country-cards) Country Intelligence Cards combine the map's currently loaded signals with country-coded feeds, regional instability data, 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. ### Choose the signal context Enable the event layers you want to analyze and set the map time range. Those choices affect the signals passed into the card. ### 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. Full centroid, baseline, infrastructure, market, and brief behavior is configured for the United States, Russia, China, Ukraine, Iran, Israel, Taiwan, North Korea, Saudi Arabia, Turkey, the United Kingdom, Germany, France, India, Pakistan, Syria, Yemen, Mexico, Colombia, Brazil, Venezuela, Egypt, the United Arab Emirates, and South Korea. Other map boundaries can be selected, but their card may contain only a header and empty or zero-value sections. ## How the card assembles data ```mermaid flowchart LR map["Current map state
Enabled layers · time range · loaded events"] radius["Centroid filter
Events within 800 km"] baseline["Curated profile
Baseline · default status · region"] cii["Regional CII
Value · delta"] code["ISO alpha-2 code"] feeds["Country-coded queries
Signals · markets · stock"] refs["Curated registries
Bases · nuclear · ports"] card["Country card"] map --> radius --> card baseline --> card cii --> card code --> feeds --> card refs --> card ``` The card does not perform polygon containment for its event widgets. It takes the events already loaded for the map and keeps events whose coordinates are within **800 km of the curated country centroid**. Events near a border can therefore appear in more than one card, while events in a large country's outer regions can fall outside the radius. The map query is capped and follows your enabled layers and selected time range. Active Signals, the timeline, and the AI brief input describe that loaded subset—not an exhaustive national event archive. ## Read the instability section The ring is an equal-weight average of four 0–100 components: | Component | What the card labels | | --- | --- | | Civil unrest | Political and social instability baseline | | Conflict | Armed-conflict baseline | | Security | Security-risk baseline | | Information | Information-environment baseline | For a profile mapped to a CII region, Y2 scales each static baseline component by the current regional CII **delta**. It then averages the four adjusted components and rounds the result. | Ring value | Ring color | | --- | --- | | 0–34 | Green | | 35–64 | Amber | | 65–100 | Red | When regional CII exists, the `NORMAL`, `ELEVATED`, or `CRITICAL` badge uses the region's raw CII value. The ring uses the profile baseline adjusted by the region's CII delta. Profiles without a mapped region use their configured default status and unadjusted baseline. The badge and ring color can therefore differ. ## Interpret signals and the timeline **Active Signals** counts every event in the 800 km loaded subset and groups the results by event category. It does not add a separate severity breakdown. The **7-Day Timeline** further limits that subset 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. If the map time range is set to 24 hours, the “7-Day” timeline can only show the 24-hour subset it received. ## 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 regional CII value, when available; - the count of currently loaded events within 800 km; - the first 10 titles from that same event subset. 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-coded feeds These widgets use the curated profile's ISO alpha-2 code instead of the 800 km radius: ### Latest signals The card shows up to eight recent stored records tagged to the country from RSS, GDELT-compatible, ACLED-compatible, URLhaus, and Feodo source types. Because threat-intelligence rows are eligible, this section can contain more than conventional news headlines. Select a row to open its source URL when one is available. ### 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, latest-signal, 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 Configure the layers and time window that feed the card 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. ### 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. ## 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. ```mermaid flowchart LR A[Scheduled source syncs] --> B[Y2 data store] C[Y2 reports and signals] --> B D[Reference datasets] --> E[Map overlays] B --> E E --> F[Markers and country cards] B --> G[Theater and FININT panels] B --> H[Ontology and API tools] ``` ## 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. ## 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. ### 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. - Related headlines, prediction markets, and a stock-index indicator. - A Markdown 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 grouped across six geographic and two thematic panels. - **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 organization workspaces, members, and audit logs. 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. ## 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 a two-day lookback and caps each regional result set before storage. 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. Personal and organization 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 | Personal, 1 member | Follow one community profile | | Lite | $5/month or $50/year | Personal, 1 member | Ongoing in-app intelligence and webhook delivery | | Pro | $20/month or $200/year | Personal, 1 member | Scheduled delivery, API integrations, audio, and advanced publishing | | Elite | $500/month or $5,000/year | Organization, up to 5 members | Shared intelligence operations with governance controls | ## 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 50 | | 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 organization 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 20 total profiles | Up to 50 total profiles | | Community subscriptions | 1 | 3 | Up to 20 total profiles | Up to 50 total profiles | | Total profiles | 1 | 4 | 20 | 50 | | 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 | 25 | | Audio narration | No | No | Yes | Yes | 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 | 5,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 5 | Up to 25 | | Default rate limit per minute | — | — | 30 | 120 | | 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 | Up to 5 | | 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 **25**. ```mermaid flowchart LR A[Active workspace] --> B[Branding template] B --> C[Assigned profile] C --> D[Report] C --> E[Email] C --> F[SMS] C --> G[Audio] ``` ## 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 | 25 | 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 ```mermaid flowchart LR basic["1. Basic information"] --> structure["2. Report structure"] structure --> advanced["3. Advanced configuration"] advanced --> create["Create feed"] create --> delivery["Review delivery settings"] ``` 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. ## 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, including a required lead section such as Bottom Line Up Front. 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. ```mermaid flowchart TD lead["Required lead section"] --> findings["Key findings or evidence"] findings --> analysis["Analysis and implications"] analysis --> outlook["Outlook or next indicators"] ``` 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. ## Scheduling flow ```mermaid flowchart LR input["Frequency, day, and UTC time"] --> cron["Stored cron schedule"] cron --> job["Scheduled report job"] job --> workflow["Research workflow"] workflow --> next["Schedule the next run"] ``` ## 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. ```mermaid flowchart LR workspace[Active workspace] --> project[Private Project] project --> chat[Project chats] project --> graph[Graph and timeline] project --> evidence[Evidence archive] project --> knowledge[Private knowledge] project --> resources[Reports and profiles] ``` ## 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 curl for reproducible API checks and jq for JSON selection. These workflows call the same public contract as an SDK without relying on a separate CLI release or local configuration format. ```mermaid flowchart LR A[Environment key] --> B[curl request] B --> C{Response type} C -->|JSON| D[jq filter] C -->|Markdown| E[file] C -->|NDJSON| F[line processor] D --> G[shell workflow] ``` ## Before you begin 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. ```mermaid flowchart LR A[OpenAPI 3.1 contract] --> B[Generated types] A --> C[Generated client] A --> D[API Workbench] A --> E[Endpoint reference] B --> F[Your transport wrapper] C --> G[Your application] F --> G ``` 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. Use curl and jq for smoke tests, exports, and automation without an SDK dependency. Explore path and query parameters, run live reads, and copy starter snippets. | Option | Best for | Contract fidelity | Main responsibility | | --- | --- | --- | --- | | 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. ```mermaid flowchart LR A[Y2_API_KEY] --> B[Python transport] B --> C[JSON envelopes] B --> D[Problem Details] C --> E[Domain workflow] D --> F[Retry or fail] ``` 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. ```mermaid flowchart LR A[openapi.yaml] --> B[y2-api.d.ts] B --> C[Typed request and response aliases] C --> D[Small fetch transport] D --> E[Application service] ``` 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 configured report-section titles, or the default BLUF structure. - 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 required 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.2` | `google/gemini-3.1-flash-lite` | | Final synthesis | `z-ai/glm-5.2` | `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 is Y2's tenant boundary for plan entitlements, limits, resources, API keys, usage, and team membership. Every account has a personal workspace. You can also own one organization workspace and join organization workspaces owned by other people. Workspace type and plan are separate: - a **personal** workspace can hold a Free, Lite, Pro, or Elite plan; - a newly created **organization** workspace starts on Free; - member seats, team governance, and workspace audit logs activate only when the organization is on Elite. ## How workspace scope works ```mermaid flowchart LR user["Signed-in user"] switcher["Active workspace"] membership["Membership check
role · seat status"] plan["Workspace plan
features · limits · budgets"] resources["Tenant resources
profiles · reports · chat · API keys · usage"] user --> switcher --> membership membership --> plan membership --> resources plan --> resources ``` The active workspace is stored on your user record. When you switch, Y2 resolves the new membership and uses that workspace for tenant-scoped reads and writes. A suspended membership is not a valid active scope. The workspace boundary covers resources such as profiles, reports, subscriptions, delivery logs, webhooks, branding and pronunciation settings, chat and generated images, API keys and their usage, investigations, OSINT watchlists, alerts, and snapshots. Changing the active workspace changes which tenant Y2 reads and writes. It does not transfer existing profiles, reports, API keys, or other resources between workspaces. ## Switch workspaces ### Open the workspace menu In the app sidebar, select the workspace name above the main navigation. ### Review the context Each entry shows its name, type, and plan. Your role is also visible under **Settings → Workspace**. ### Select a workspace Choose another active membership. The app updates the active tenant context immediately. ### Confirm before making changes Check the workspace name again before creating a profile, API key, webhook, or investigation. The new resource is assigned to the active workspace. ## Create an organization workspace Open the workspace menu or **Settings → Workspace**, then select **New organization**. Enter a name from 2 to 80 characters. The organization becomes active after creation and starts on Free. Your personal workspace remains separate. To enable team seats, either upgrade the organization to Elite or move an existing paid personal plan to it and ensure that plan is Elite. The current creation path reuses the organization workspace you already own instead of creating a second owned organization. You can still be a member of additional organizations owned by other users. ## Move a paid plan to an organization If you own a paid personal workspace and a Free organization workspace, **Settings → Workspace** shows **Move workspace plan**. ### Select the organization target You must be the owner of both the paid personal workspace and the target organization. ### Move the plan Y2 attaches the active paid subscription and its plan entitlements to the organization, copies the Stripe customer reference when needed, and makes the organization active. ### Review both workspaces The source personal workspace is demoted to Free. Its existing tenant resources stay there; only the billing plan moves. Moving a Lite or Pro plan does not unlock team seats because organization membership management is an Elite feature. ## Roles and enforced 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 and revoke invites | Yes | Yes | No | No | | View governance and audit activity | Yes | Yes | No | No | | Create and manage workspace API keys | Yes | Yes | No | No | | Change another member's role | Yes | No | No | No | | Remove another member | Yes | No | No | No | | Manage billing or move the paid plan | Yes | No | No | No | Owners cannot be removed or assigned another role. A non-owner can remove their own membership. Organization deletion is currently available to both owners and admins when every deletion blocker has been cleared. Y2 checks role requirements at each resource operation. For example, owners and admins can manage all workspace profiles; members can write profiles they created; viewers do not have profile write access. Team investigations also distinguish read membership from owner/member write access. Review the relevant feature guide instead of treating the role name alone as a guarantee for every object type. ## Invite teammates on Elite Only an Elite organization exposes active team management. ### Open Members Switch to the organization, then go to **Settings → Members**. Owners and admins can create invitations. ### Choose the email and role Select `admin`, `member`, or `viewer`. The owner role cannot be invited. The owner plus active members and unexpired open invitations must fit within the five-seat cap. ### Send the invitation Y2 creates a unique token, emails the invite, and keeps it valid for seven days. Inviting the same email while an open invite exists resends the existing invite. ### Accept with the matching account The signed-in account's normalized email must match the invited email. Acceptance preserves or creates a separate personal workspace, adds the organization membership, and switches the invitee into the organization. Owners can later change non-owner roles or remove members. Owners and admins can revoke pending invitations. ## Seat limits and downgrade behavior Elite includes **five active seats total**, including the owner. An unexpired pending invitation reserves a seat; suspended members do not. If the workspace falls to a one-seat plan, Y2 keeps the owner active and suspends the remaining members in join order. Suspended members: - cannot use the organization as their active workspace; - disappear from their workspace switcher; - retain a membership record for possible restoration. When the workspace returns to a plan with more seats, Y2 reactivates suspended members in the same deterministic order until the new cap is full. ## Govern an Elite organization Owners and admins can open **Settings → Governance** to review: - active seats, API-key count, current-period event count, and plan limits; - per-member event counts and cost units for the current UTC month; - every workspace API key's prefix, scopes, state, request count, creator, expiry, and latest use time and IP; - the latest 50 workspace audit records. The audit log records workspace membership actions such as invite creation, acceptance, approval, revocation, role changes, and member removal. It also records material Project and Automation changes with the acting user's identity, including Project lifecycle and context changes, evidence and graph actions, Automation definition changes, and manual run requests. It is not a complete log of every report, chat message, model call, or API operation. Projects and Automation definitions remain owner-private in this release. Audit visibility gives Elite administrators governance evidence; it does not grant access to another member's private Project content. ## Workspace API keys and limits New API keys require a Pro or Elite active workspace. Keys created in an organization are scoped to that organization's resources and can be managed only by owners or admins. Elite organization limits are shared across the tenant: | Limit | Elite value | | --- | ---: | | Active seats | 5, including the owner | | Total profiles | 50 | | API keys | 25 | | API requests | 120/minute and 50,000/day | | Chat budget | 5,000 credits/month | Creating or rotating more keys does not multiply the workspace's aggregate request limits or usage budget. ## Delete an organization In **Settings → Workspace**, an owner or admin can delete the active organization after typing its name. Personal workspaces cannot be deleted. Deletion is blocked while the organization still has an active or unresolved billing subscription or tenant resources such as profiles, reports, delivery records, webhooks, keys and usage logs, chat artifacts, investigations, OSINT workspace data, or other dependent records. Clear the checklist shown in the app first. Large cleanup sets can require support rather than an in-app deletion. After the blockers are cleared, deletion removes the organization record, memberships, invitations, audit logs, and workspace rate counters. Affected users fall back to another active membership when one exists. ## Related guides Compare workspace features, seats, API keys, and usage limits Create and protect workspace-scoped credentials Understand profile ownership and workspace capacity