# Y2 Affiliate Program Terms (2026-09-24-v5) (/docs/affiliate-program-terms-2026-09-24-v5)
# Y2 Affiliate Program Terms
**Version:** `2026-09-24-v5`
**Effective time:** The fixed UTC time shown in Settings → Referrals. The time is recorded in
Y2's production policy configuration when this version takes effect.
These Affiliate Program Terms supplement the Y2 Terms of Service between YEETUM, LLC, doing
business as Y2 (“Y2”), and the participating Y2 account holder (“Promoter”). They govern
referral commissions under this version. A payment's verified first collection time determines
its commission policy. Payments before the effective time retain their applicable prior terms:
the accepted `2026-09-17-v4` native policy covered Lite and Pro, and protected imported
provider liabilities retain their original terms. This version does not recalculate, erase, or
newly commission earlier payments.
## 1. Joining and account standing
Participation is voluntary. You must affirmatively accept the current terms before your
assigned referral code and link become active. To earn commission on a payment under this
version, you must have accepted version `2026-09-24-v5` no later than that payment's verified
collection time. You must have a verified email, own a billing workspace with a paid, active,
current Lite, Pro, or Elite subscription, maintain your account in good standing, and satisfy
Y2's identity, tax, and payout-provider requirements. Membership in someone else's paid
workspace does not qualify you. Your own Lite subscription is sufficient; the Pro/Elite
restriction concerns the referred customer's purchase.
Y2 records your acceptance, attribution, payments, adjustments, and payout decisions. Joining,
sharing a link, or a displayed estimate does not guarantee an approved or payable commission.
## 2. Qualifying referred customers
A referral must be a bona fide new customer who uses your assigned link or accepted code before
their first paid Y2 subscription payment. The customer must be a separate person and account;
self-referrals, duplicate or fabricated accounts, manipulated attribution, and customers who
paid before the saved claim do not qualify. Optional browser link capture can last up to 90
days. A valid saved claim cannot be replaced and expires after 90 days for its first qualifying
payment.
For payments first collected at or after the effective time, only a successful, non-zero Pro
or Elite subscription purchase can create the first qualifying conversion and commission.
A Free or Lite purchase, signup, trial, fully discounted invoice, or uncollected payment earns
nothing under this version. A referred customer's paid Lite activity remains visible in the
referral dashboard. If that same attributed subscription upgrades to Pro or Elite and its first
qualifying payment is collected within the original 90-day claim window, that upgrade may be
the first qualifying conversion. Earlier Lite invoices remain noncommissionable under this
version. Renewals of the qualifying attributed subscription may earn recurring commissions
while all conditions remain satisfied; they do not add another distinct customer conversion.
Attribution does not follow a customer to a different subscription automatically.
## 3. Commission calculation and corrections
The commission is 15% of eligible Pro or Elite subscription revenue actually collected by Y2,
after excluding taxes, discounts and credits and subtracting actual attributable incoming
Stripe payment-processing fees. Commission rounds down to the smallest unit of the payment
currency. For example, a $50.00 eligible payment with no tax and a hypothetical $2.00
processing fee produces $48.00 net revenue and a $7.20 commission. The example is not a
promise of a Stripe fee.
If fee, attribution, identity, payment, or historical evidence is incomplete, Y2 may hold the
calculation for review. Refunds, credits, chargebacks, lost disputes, fee corrections, and
other reversals may reduce or remove commission, including after a payout through an offset
against future earnings. Y2 will keep an auditable record of the reason and adjustment.
An existing amount is not recomputed solely because these terms changed.
## 4. Review and payout
Commission remains under review for at least 60 days after the qualifying payment and may
remain held while evidence or a dispute is unresolved. The initial payout route supports
verified US recipients and USD. A request requires at least $100 in eligible commission after
adjustments and before the payout deduction, a current eligible Y2 account, a ready Stripe recipient,
complete historical reconciliation, and a verified current cost tariff. Payout availability,
supported countries and currencies may change prospectively after notice.
For each USD payout batch under this version, Y2 deducts **3% of the batch's gross eligible
commission plus $0.50**. The percentage component rounds up to the nearest cent. This is the
entire affiliate payout deduction: actual Stripe sending costs are not added on top, and the
deduction does not change when those costs are reported or corrected later. For example, a
$100.00 gross commission batch has a $3.50 deduction and a $96.50 transfer to Stripe. Y2 bears
any difference between this deduction and its actual sending costs. Incoming Stripe
payment-processing fees were already subtracted when commission was calculated and are not
charged again at payout. Payout batches earned under prior terms retain their own cost rules.
Y2 withholds this fixed deduction before sending the transfer. The dashboard distinguishes gross
commission, deduction, transfer to Stripe, and confirmed bank delivery. A Stripe transfer does
not itself prove bank delivery. Y2 does not debit your bank account for a later change in its
Stripe sending costs.
## 5. Responsible promotion
Describe Y2 accurately. Clearly and conspicuously disclose your financial connection with
each endorsement, near the recommendation or referral link; for example, “I may earn a
commission if you buy Y2 through my link.” A disclosure only in a profile, footer, or separate
terms page is insufficient. Follow applicable advertising, endorsement, privacy, anti-spam,
consumer-protection, and platform rules. Do not impersonate Y2, send spam, use deceptive
redirects, stuff cookies, manipulate attribution, bid on Y2 trademarks without permission,
offer an unapproved incentive, or make false, unsubstantiated, or guaranteed claims.
Y2 may review, hold, reverse, suspend, or end participation for suspected fraud, abuse,
nonpayment, misleading promotion, or legal or provider risk. Y2 will review available evidence
before a final decision and will preserve records of existing liabilities and lawful offsets.
You control how you promote Y2, bear your own expenses, and may not bind Y2. Participation is
not employment, an investment, a franchise, or a guarantee of income.
## 6. Taxes, records, changes, and disputes
You are responsible for taxes on amounts received and for accurate payout and tax information.
Y2 or its provider may request verification and withhold payout where required by law or while
required information is incomplete. Y2 may retain financial, attribution, acceptance, and
review records as described in its privacy notice and applicable law.
Y2 may change eligible plans, rates, costs, review periods, thresholds, or these terms
prospectively. Material changes require publication in the product and acceptance where
applicable. Changes do not retroactively alter a payment's governing policy or approved
historical liability. Termination or account deletion stops future participation but does
not silently erase valid balances, adjustments, tax obligations, audit rights, or required
financial records. The general dispute, liability, and governing-law provisions of the Y2
Terms of Service apply unless a program-specific term here controls.
By selecting the acceptance checkbox and confirming in Settings → Referrals, you agree to
version `2026-09-24-v5`. The English text controls. A Spanish-language summary is available
in Settings → Referrals; it does not replace these complete terms.
---
# Join and Manage the Affiliate Program (/docs/affiliate-program)
This guide shows you how to join the Y2 Affiliate Program and manage your referral link from
[**Settings → Referrals**](/app/settings/referrals).
Review your current eligibility, accept the program terms, and manage your referral activity.
Compare the Y2 plans that can qualify an account for the program.
Read the [complete v5 Affiliate Program Terms](/docs/affiliate-program-terms-2026-09-24-v5)
and the UTC effective time in **Settings → Referrals** before accepting or sharing a link.
The Spanish text in Settings is a summary; the complete English terms control.
## Before you start
Sign in with the account that owns the paid billing workspace you want to use. Referral settings
will show any requirement that still needs attention, including:
- A verified account email.
- Ownership of a qualifying paid workspace.
- A current subscription payment for that workspace.
Your own current paid Lite, Pro, or Elite subscription can qualify you to participate. For
referred customer payments from the Pro/Elite policy's displayed UTC effective time, only a
Pro or Elite purchase can earn commission. A referred Lite purchase remains visible as a
referral but earns nothing under the new policy. A later Pro or Elite upgrade can become its
first qualifying conversion if its payment falls within 90 days of the saved claim.
Admin or member access to another customer's paid workspace does not qualify the account. Use
[Account settings](/app/settings/account) to resolve email requirements and
[Billing settings](/app/settings/billing) to resolve plan or payment requirements.
## Join the program
### Open referral settings
Go to [**Settings → Referrals**](/app/settings/referrals). The program card shows whether your
account is ready to enroll or which requirement needs attention.
### Review the current terms
Expand **Review the affiliate program terms**. Review the version, commission rules, payout
requirements, and responsible-promotion rules shown in the app.
### Accept and join
Select the agreement checkbox, then choose **Join and create my referral link**. Y2 records the
terms version you accepted before starting link creation.
### Copy the activated link
Wait for your complete referral URL to appear, then choose **Copy link**.
## Manage your referral activity
Return to [**Settings → Referrals**](/app/settings/referrals) whenever you need to:
- Copy your current referral link.
- Review saved referrals, including those waiting on a qualifying purchase, conversions,
active qualifying subscriptions, and expired or rejected claims.
- Check payout eligibility requirements.
- Review verified payments, commission calculations, and payout statuses.
For each USD payout batch under the current Pro/Elite terms, Y2 deducts 3% of the batch's gross
commission plus $0.50, rounding the percentage up to the cent. A $100.00 commission batch
therefore sends $96.50 to Stripe. This is the full affiliate payout deduction; later changes to
Stripe's actual sending costs do not change it. Earlier earnings keep their accepted payout rules.
Always share the complete link displayed in the app. Do not reconstruct or shorten its referral
token. Disclose that you may earn a commission clearly and close to every endorsement or
referral link, such as “I may earn a commission if you buy Y2 through my link.”
## Keep your account eligible
Use the **Payout eligibility** card in referral settings as your checklist. Keep every displayed
requirement satisfied while participating and when commissions are reviewed or paid. If the card
shows an action is required, resolve that item before relying on new referral activity.
## Troubleshoot enrollment or synchronization
Verify the email in **Settings → Account**, then reopen **Settings → Referrals**.
Confirm that this account owns the billing workspace, then restore the qualifying plan or
subscription in **Settings → Billing**.
Wait briefly and reopen referral settings. If Stripe onboarding displays **Retry**, choose
it after the issue has been resolved.
Follow the message shown in the app. If it requests support review, contact
[support@y2.dev](mailto:support@y2.dev) and include the displayed error text.
---
# Agent Y2 API (/docs/api/agent-y2)
Agent Y2 exposes the same preconfigured intelligence agent used by the Y2 copilot through two
API-key-authenticated streaming endpoints. It uses the key's workspace, plan entitlements, shared
Agent Y2 credit balance, persisted thread history, and allowed Y2 tools.
API callers cannot select an arbitrary model, enable onboarding mode, or attach files in v1. The
`model` value on the OpenAI-compatible route is a Y2 routing alias, not model selection. Requests
without client tools use the fixed Agent Y2 Copilot instructions. A non-empty `tools` array
selects the isolated client-harness contract described below.
## Choose an endpoint
Both endpoints use `https://api.y2.dev/api/v1`.
| Endpoint | Stream format | Choose it when |
| --- | --- | --- |
| `POST /agent-y2/chat/stream` | Vercel AI SDK UI message stream | Your client understands the native AI SDK protocol |
| `POST /chat/completions` | OpenAI-style `chat.completion.chunk` server-sent events | Your client already consumes streaming chat completions |
```mermaid
flowchart LR
client["API client"] --> auth["API key + agent:y2"]
auth --> limits["Key, account, and organization-credit checks"]
limits --> context["Workspace thread + entitlements"]
context --> agent["Fixed Agent Y2 model and tools"]
agent --> format{"Response format"}
format --> native["AI SDK UI stream"]
format --> openai["OpenAI-compatible SSE"]
```
The routes share the same Agent Y2-specific rate-limit pool. Switching formats does not create a
second quota.
## Create a scoped key
### Open API Keys
In the workspace that should own the threads and tool actions, open **Settings → Developers →
API Keys**.
### Grant `agent:y2`
Create a dedicated key with the `agent:y2` scope. New API-key creation requires Pro or Elite.
Existing Lite keys remain usable only with the scopes already granted to them.
### Store the key on a trusted server
Send it as `Authorization: Bearer $Y2_API_KEY`. Do not put an Agent Y2 key in browser bundles,
prompts, metadata, logs, or public repositories.
Agent Y2 does not support x402. A Y2 `402` with code `INSUFFICIENT_CREDITS` means the workspace's
shared Agent Y2 balance is exhausted; it is not a payment challenge.
## Send a native request
```bash title="Terminal"
curl -N -i "https://api.y2.dev/api/v1/agent-y2/chat/stream" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"parts": [
{ "type": "text", "text": "What changed in cyber risk this week?" }
]
}
],
"metadata": {
"source": "customer_crm",
"externalThreadId": "case-123"
}
}'
```
The response is an AI SDK UI message stream. API responses omit internal reasoning parts even
though the in-app stream can include them.
Read the `X-Thread-Id` response header and store it. Continue the conversation by sending that ID
in the next native request body:
```json title="request.json"
{
"threadId": "j57...",
"messages": [
{
"role": "user",
"parts": [{ "type": "text", "text": "Which entities should I watch next?" }]
}
]
}
```
## Send an OpenAI-compatible request
```bash title="Terminal"
curl -N -i "https://api.y2.dev/api/v1/chat/completions" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "y2-agent",
"stream": true,
"messages": [
{
"role": "user",
"content": "Summarize the latest Y2 platform capabilities."
}
]
}'
```
The compatibility route requires `stream: true`. `model` is optional; when supplied, it must be
`y2-agent` or `agent-y2`. It emits a role chunk, text or tool-call delta chunks, a final chunk with
`finish_reason: "stop"` or `"tool_calls"`, and `data: [DONE]`.
To continue a conversation, pass `threadId` in the JSON body or send the prior ID in an
`X-Thread-Id` request header.
Supplying a non-empty `tools` array switches this request to client-harness mode. Y2 sends the
schemas to the model and streams function calls back, but it never executes those functions.
Your client remains responsible for permission checks, local execution, and returning matching
tool results. Agent Y2's server-side intelligence tools are not exposed in this mode.
## Use client-side tools
Client-harness mode preserves system and developer instructions, assistant tool calls, and matching
tool results. This is the mode used by the native Y2 CLI for repository instructions, local file
operations, commands, skills, and subagents.
```bash title="Terminal"
curl -N -i "https://api.y2.dev/api/v1/chat/completions" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "y2-agent",
"stream": true,
"messages": [
{ "role": "system", "content": "Inspect the workspace before editing." },
{ "role": "user", "content": "Read README.md." }
],
"tools": [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read a workspace file",
"parameters": {
"type": "object",
"properties": { "path": { "type": "string" } },
"required": ["path"]
}
}
}
],
"tool_choice": "auto"
}'
```
The response header `X-Y2-Agent-Mode: harness` confirms that the client-tool contract was selected.
Tool definitions are limited to 128 functions. Names, descriptions, schemas, transcript length, and
tool arguments are bounded; malformed or unmatched tool history returns `400` before model execution.
## Understand thread context
| Request state | Context used by Agent Y2 |
| --- | --- |
| No `threadId` | Creates an API-source thread and uses non-empty user and assistant text from the supplied transcript |
| Existing `threadId` | Verifies the same user, API source, and workspace, then loads up to 40 persisted messages from that thread |
| Latest user message | Saved to the thread and used as the new prompt |
| System, developer, or tool transcript entries without `tools` | Accepted for compatibility but excluded from the fixed Copilot prompt |
| Non-empty `tools` array | Uses client-harness mode with supplied instructions and complete matching tool history |
A thread created in the app cannot be continued through the API. A workspace-scoped API key also
cannot continue a thread from another workspace.
The server returns `400` for an invalid ID shape, `404` for a missing thread, and `403` when the
thread exists outside the caller's allowed source, user, or workspace scope.
## Available tools
The exact tool set is assembled for every request from current workspace entitlements.
| Tool category | Availability |
| --- | --- |
| Y2 documentation search | Always registered |
| Public briefing search and follow | Registered for standard Agent Y2 sessions |
| News search | Registered for standard sessions |
| Web search | Requires the workspace web-search entitlement |
| Profile creation and editing | Requires remaining custom-profile capability and metering context |
| OSINT search | Requires OSINT entitlement |
| Y2 Global Knowledge retrieval | Requires Chat entitlement; searches only authorized shared and key-workspace corpora |
| Cyber, FININT, markets, entity, incident, and investigation tools | Added according to the related intelligence entitlements |
| Image generation | Requires image-generation entitlement and a compatible selected agent path |
Tool availability does not guarantee that the agent will call a tool. Tool actions still enforce
the underlying workspace permissions and resource limits. This table applies to Copilot mode;
client-harness mode advertises only the caller's function schemas and executes none of them on Y2.
Agent Y2 uses its existing `agent:y2` scope when it invokes Y2 Global Knowledge. Do not add
`intel:knowledge` to an Agent Y2 key unless the same integration also calls the direct retrieval
endpoint. Direct retrieval and agent execution are separate least-privilege surfaces.
## Metadata behavior
Both routes accept a strict metadata object:
```json
{
"metadata": {
"source": "ops_console",
"externalUserId": "analyst-42",
"externalThreadId": "case-123"
}
}
```
| Field | Maximum | Current behavior |
| --- | --- | --- |
| `source` | 128 characters | Accepted for compatibility; the thread source is still recorded as `api` |
| `externalUserId` | 256 characters | Accepted but not currently persisted to the thread |
| `externalThreadId` | 256 characters | Stored when Y2 creates a new thread; it does not replace the Y2 `threadId` |
Unknown metadata properties fail strict validation. Keep your own correlation log and never put
secrets or full customer records in metadata.
## Provider privacy controls
The current Agent Y2 executor selects chat models marked as Zero Data Retention capable and sends
OpenRouter provider options with `zdr: true` and `data_collection: "deny"`.
These controls govern the external model request. Y2 still stores API-source threads, messages,
request attribution, tool results, and usage records under Y2's product data lifecycle. See the
[Privacy Policy](/privacy) for the customer-facing data terms.
## Rate limits and response headers
Agent Y2 applies these limits in addition to the API key's normal plan limits:
| Limit scope | Per minute | Per day |
| --- | ---: | ---: |
| API key | 5 | 100 |
| User or workspace aggregate | 10 | 250 |
Inspect `Retry-After`, the normal `X-RateLimit-*` headers, and the
`X-Y2-Agent-RateLimit-*` headers. Successful streams also return `X-Thread-Id`, `X-Y2-Agent: y2`,
and `X-Y2-Agent-Mode: copilot` or `X-Y2-Agent-Mode: harness`.
## Error guide
| Status | Common cause | Response |
| --- | --- | --- |
| `400` | Invalid JSON, missing latest user text, unsupported alias, invalid tool schema or history, invalid thread ID, or native `modelId`, `isOnboarding`, or `attachments` | Correct the request; do not retry unchanged |
| `401` | Missing, malformed, revoked, or invalid API key | Replace or rotate the credential |
| `402` | Subscription, workspace Agent Y2 credit, or upstream provider credit exhaustion | Check the workspace plan and Agent Y2 credit balance; do not start x402 handling |
| `403` | Missing scope, no chat entitlement, blocked access, or unauthorized thread | Check key scope, workspace, plan, and thread origin |
| `404` | Supplied thread no longer exists | Start a new thread or correct the stored ID |
| `429` | Normal API or shared Agent Y2 limit exceeded | Back off for `Retry-After` seconds |
| `500` | Stream preparation or provider execution failed | Retry with backoff and retain `X-Request-Id` for support |
## Related reference
Verify the exact request schemas, response headers, and generated examples
Understand key format, scopes, tenant resolution, and plan limits
Connect an MCP-compatible agent to Y2's exposed tools
---
# AI Quickstart (/docs/api/agentic-dx)
Use this page before asking a coding agent to build against Y2. It gives the agent stable discovery
URLs, separates the v1 and v2 API roots, identifies the required scopes, and keeps credentials out
of the prompt.
The button copies a short bootstrap prompt that tells the agent to read this page and the MCP guide.
It intentionally links to current documentation instead of embedding a second copy of plan limits
that can drift.
## Agent build loop
### Discover the documentation
Start with `https://y2.dev/llms.txt`. It is the compact index generated from the documentation
tree. Load `llms-full.txt` only when the task needs broad cross-product context.
### Read the relevant guide and operation
Use the manual guide for intent and workflow, then use the generated endpoint reference or raw
OpenAPI operation for the exact path, method, parameters, scopes, schemas, x402 setting, and
response headers.
### Resolve authentication and tenancy
Create the API key in the workspace that should own the request. Grant only the documented
scopes. Let Y2 derive user and workspace authorization from the key; never treat a caller-
supplied user or workspace ID as proof of access.
### Implement from the contract
Keep `Y2_API_KEY` in a server or local agent environment. Generate types from OpenAPI when
useful, but preserve Y2 response headers and structured errors in the integration layer.
### Verify one real request
Run a bounded curl request or use the API Workbench. Confirm the status, response shape,
`X-Request-Id`, API version, pagination, and rate-limit headers before expanding the workflow.
## Canonical context URLs
| Resource | URL | Use |
| --- | --- | --- |
| Documentation index | `https://y2.dev/llms.txt` | Discover pages without loading the full corpus |
| Full documentation | `https://y2.dev/llms-full.txt` | Broad context for cross-cutting work |
| Individual raw page | `https://y2.dev/docs/{path}.mdx` | Load one page as Markdown |
| API guide | `https://y2.dev/docs/api` | Understand surfaces, auth choices, and workflow boundaries |
| Generated reference | `https://y2.dev/docs/api/reference` | Inspect operation-specific schemas and examples |
| OpenAPI 3.1 document | `https://y2.dev/api/openapi.yaml` | Generate clients and validate exact contracts |
| Authentication guide | `https://y2.dev/docs/api/authentication` | Choose key scopes and understand tenant resolution |
| Agent Y2 guide | `https://y2.dev/docs/api/agent-y2` | Integrate native or OpenAI-compatible agent streams |
| MCP guide | `https://y2.dev/docs/api/mcp` | Configure the separately distributed local MCP server |
`llms.txt`, `llms-full.txt`, and raw `.mdx` pages are generated from `content/docs` during the site
build and served by the local Vite documentation middleware during development.
Manual guides explain goals and safe usage. If a hand-written example conflicts with a generated
operation, use `public/api/openapi.yaml` and the generated endpoint page as the field-level source
of truth, then report the stale guide.
## Base URLs
| Surface | Base URL | Examples |
| --- | --- | --- |
| Core v1 | `https://api.y2.dev/api/v1` | Reports, profiles, news, OSINT, webhooks, subscriptions, Agent Y2 |
| Intelligence v2 | `https://api.y2.dev/api/v2` | Changes, incidents, entities, graphs, markets, FININT, signals, cyber |
The OpenAPI document declares an operation-level `https://api.y2.dev` server for v2 paths because
those paths already include `/api/v2`. Do not combine the v1 base with a v2 operation path.
## API-key plan and scope map
This table describes the new API-key path. An endpoint that explicitly supports x402 can also be
called without an API key by completing that endpoint's payment flow.
| Surface | New key minimum | Required scope |
| --- | --- | --- |
| Reports | Pro | `reports:read` |
| Report audio | Pro | `reports:audio` |
| Profiles | Pro | `profiles:read` or `profiles:write` by operation |
| News | Pro | `news:read` |
| OSINT v1 | Pro | `osint:read` |
| FININT and markets v2 | Pro | `intel:finint` |
| Cyber v2 | Pro | `intel:cyber` |
| Explorer entities, incidents, and graphs | Pro | `intel:explorer` |
| Webhook management API | Pro | `webhooks:manage` |
| Agent Y2 stream | Pro | `agent:y2` |
| MCP docs and OpenAPI resources | None | None |
| MCP data or Agent Y2 tools | Pro for a new key | Scope used by each underlying API operation |
Lite includes in-app FININT, Cyber, Explorer, full Situation Room access, and webhook delivery, but
cannot create new API keys. A Lite workspace with existing keys retains those credentials and their
historical limit of 10 requests per minute and 500 per day. This grace does not grant new scopes or
new key creation.
| Plan | New keys | Default per-key request limits |
| --- | ---: | ---: |
| Free | 0 | No key access |
| Lite | 0 | Existing keys only: 10/minute and 500/day |
| Pro | 10 | 30/minute and 5,000/day |
| Elite | 100 | 300/minute and 50,000/day |
Elite workspaces include five seats, with larger organizations available by
arrangement. Workspace members share tenant resources and account-level rate-limit aggregates;
creating more keys does not multiply the workspace aggregate quota.
## Give an agent safe instructions
Use this compact task template after the bootstrap prompt has loaded the docs:
```text title="Agent task template"
Implement [specific Y2 workflow] in this codebase.
Before editing:
1. Read the relevant Y2 manual guide.
2. Read the exact operation in https://y2.dev/api/openapi.yaml.
3. Confirm the base URL, required scope, pagination, x402 support, and error responses.
Constraints:
- Read Y2_API_KEY from the server or MCP client environment only.
- Never put the key in source, browser code, prompts, tool arguments, or logs.
- Let the API key determine tenant access.
- Use the narrowest scopes and bounded page sizes.
- Handle 401, 402, 403, 404, 409/412 where documented, 429, and 5xx.
- Preserve X-Request-Id and Retry-After for diagnostics.
Verify with one bounded request and report the observed status and response shape.
```
## MCP boundary
The platform repository defines the HTTP APIs, OpenAPI contract, docs, billing rules, and Agent Y2
executor. `@y2-intel/mcp` is documented as a separate stdio package and its implementation is not
present in this repository.
Follow the [MCP guide](/docs/api/mcp) for the intended configuration. Before automating against a
specific package release, inspect its actual `tools/list`, `resources/list`, and `prompts/list`
responses instead of assuming every documented capability exists in the installed version.
When configuring a local MCP client, pass `Y2_API_KEY` through the client environment. Grant
`agent:y2` only when the agent should be able to use entitled account actions; documentation and
OpenAPI discovery do not require that scope.
## Smoke tests
```bash title="Terminal"
curl -sS -D - "https://api.y2.dev/api/v1/reports?limit=1" \
-H "Authorization: Bearer $Y2_API_KEY"
```
```bash title="Terminal"
curl -sS -D - "https://api.y2.dev/api/v2/finint?limit=1" \
-H "Authorization: Bearer $Y2_API_KEY"
```
Use a key that has the matching read scope. `-D -` prints the response headers so the agent can
verify request IDs, API version, and remaining quota instead of checking only the JSON body.
## Related guides
Choose the right API surface and authentication path
Understand key format, scopes, IP rules, limits, and workspace tenancy
Generate clients and maintain the contract
Run a live request and copy environment-based snippets
---
# Authentication (/docs/api/authentication)
Y2 authenticates most API requests with a workspace-bound bearer key. Endpoints that explicitly
support [x402](/docs/api/x402) also offer a separate paid request path when no bearer key is sent.
## Authentication flow
```mermaid
flowchart LR
request["HTTP request"] --> bearer{"Authorization starts\nwith Bearer?"}
bearer -->|Yes| key["Validate hash, state, plan, IP"]
key --> scope["Check operation scope"]
scope --> limits["Increment key + tenant limits"]
limits --> handler["Run in key workspace"]
bearer -->|No| payment{"Endpoint supports x402?"}
payment -->|Yes| x402["Payment challenge or signed retry"]
payment -->|No| unauthorized["401"]
```
## Plan requirements
| Plan | New keys | Default limit per key | Notes |
| --- | ---: | ---: | --- |
| Free | 0 | No key access | Use in-app features or supported x402 endpoints |
| Lite | 0 | Existing keys: 10/minute and 500/day | Grandfathered keys only; historical active-key capacity is 2 |
| Pro | 10 | 30/minute and 5,000/day | New API-key access |
| Elite | 100 | 300/minute and 50,000/day | New API-key access plus team governance |
Existing keys that were already eligible under the Lite grace policy remain visible, usable,
rotatable, and revocable. A later downgrade from Pro or Elite marks keys plan-ineligible; it does
not create new Lite grace access.
Rate limits apply per key and again to the account aggregate: the workspace for workspace-bound
keys, or the user for a legacy unscoped key. Creating more keys does not multiply the tenant quota.
## Create a key
### Select the correct workspace
Use the workspace switcher first. Y2 binds the new key to the active workspace, and later API
requests resolve their tenant from that stored binding.
### Open API Keys
Go to [**Developers → API Keys**](/app/developers/api-keys). Creating a key requires an owner or
admin role in the workspace and a Pro or Elite plan.
### Name and scope the key
Use a unique operational name such as `report-exporter-prod`. Select only the scopes required
by that integration. At least one scope is required.
### Add exact IPs when useful
Optionally add the stable public egress IP addresses that are allowed to use the key. Confirm
the deployed service's actual egress address before enabling this restriction.
### Copy the secret once
Y2 displays the full key only in the creation result. Store it in a secret manager or protected
environment variable before closing the dialog.
## Key format and storage
Y2 generates 32 random bytes and encodes them as 64 hexadecimal characters after the `y2_`
prefix:
```text
y2_{64 lowercase hexadecimal characters}
```
Only the SHA-256 hash and a short display prefix are stored. The dashboard cannot retrieve the
plaintext later. Regeneration creates new secret material and immediately invalidates the prior
value.
## Send the key
```bash title="Terminal"
curl -sS -D - "https://api.y2.dev/api/v1/reports?limit=1" \
-H "Authorization: Bearer $Y2_API_KEY"
```
The authentication scheme is the case-sensitive `Bearer ` prefix followed by the key. Keep the
credential server-side; never place it in a browser bundle, query string, prompt, tool argument, or
log message.
[API Workbench](/app/developers/api-workbench) keeps the pasted value in React component state and
sends it only when you run a request. Generated curl, TypeScript, and Python snippets continue to
reference `Y2_API_KEY`. Treat the browser session and screen as sensitive while the key is
present.
## Workspace scope
| Boundary | Current behavior |
| --- | --- |
| Resource access | Profiles, subscriptions, reports, Projects, Automations, webhooks, threads, public IDs, and usage are resolved in the key's stored workspace where the operation is tenant-scoped |
| Key management | Workspace owners and admins can list and manage workspace keys; other roles receive no key list |
| Aggregate limits | All keys in a workspace contribute to the same plan-level minute and daily pool |
| Workspace switching | Switching the application workspace does not move an existing key; it changes where a newly created key is bound |
Do not accept a user-provided `workspaceId` as authorization. If an operation accepts a resource ID,
the backend still verifies that the resource belongs to the tenant resolved from the key.
## Scopes
| Scope | Grants access to |
| --- | --- |
| `reports:read` | Report lists, individual reports, text, signals, and report graphs where specified |
| `reports:audio` | Report audio and audio-text operations |
| `profiles:read` | Listing profile subscription records in the key workspace |
| `profiles:write` | Creating and changing owned profiles, including destructive profile deletion |
| `projects:read` | Listing and inspecting owned Projects in the key workspace |
| `projects:write` | Creating, changing, archiving, restoring, and pinning owned Projects |
| `automations:read` | Listing owned Automation definitions and run history |
| `automations:write` | Creating, changing, archiving, and manually running owned Automations |
| `news:read` | News items, recaps, and feed discovery |
| `webhooks:manage` | Webhook configuration and subscription-delivery webhook operations |
| `osint:read` | v1 Situation Room endpoints and the v2 change feed |
| `intel:finint` | v2 FININT, markets, shared incidents, and shared signals |
| `intel:cyber` | v2 cyber graph, CVEs, threat actors, shared incidents, and shared signals |
| `intel:explorer` | v2 entities, entity graphs, shared incidents, and shared signals |
| `intel:knowledge` | Direct bounded retrieval across authorized Y2 Global Knowledge corpora |
| `agent:y2` | Native and OpenAI-compatible Agent Y2 streams with entitled account tools |
| `ledger:read` | Workspace ontology ledger folds, record exports, seal verification, and STIX 2.1 exports |
| `ledger:write` | Opening ledger subjects and appending designators, observations, and claims |
An array of required Intel scopes in the generated reference means **any one** of the listed scopes
can authorize that shared endpoint. Always check the exact operation's `x-required-scopes` value.
Plan entitlements are enforced again during key validation. Storing a scope on a key does not keep
that capability active after the workspace loses the required plan feature.
`intel:knowledge` and `agent:y2` are intentionally separate. Direct
`POST /api/v2/intel/knowledge/retrieve` calls require `intel:knowledge`. Agent Y2 routes continue to
require only `agent:y2`; an entitled agent can invoke Global Knowledge through the shared internal
tool. Existing keys are never expanded automatically—edit or rotate a key deliberately when an
integration needs `intel:knowledge`, `projects:*`, `automations:*`, or `ledger:*`.
Workspace research endpoints require a workspace-bound key: company financial history
(`intel:finint`), cyber-market fusion (`intel:explorer`), and the ontology ledger (`ledger:*`). A
personal key receives `403`, and x402 payment is not accepted for them.
`profiles:write`, `projects:write`, `automations:write`, `ledger:write`, `webhooks:manage`, and
`agent:y2` are not
read-only. Isolate them from reporting keys and grant them only to integrations designed to
perform the related actions.
## IP allowlisting
The dashboard's IP restriction currently performs an exact string comparison against the client IP
resolved from `CF-Connecting-IP`, the first `X-Forwarded-For` value, or `X-Real-IP`.
- Enter individual IPv4 or IPv6 addresses only.
- CIDR ranges are not parsed by the current middleware.
- For hosted jobs, allow the service's public egress IP, not a private container address.
- Removing every entry disables the key-specific IP restriction.
- A resolved address not present in the list returns `403` with `IP_NOT_ALLOWED`.
Core API responses use permissive CORS headers, and stored `allowedOrigins` metadata is not
currently enforced by request authentication. Use exact IP restrictions where appropriate and
keep keys out of client-side applications.
## x402 selection
On an x402-enabled operation, the request path is selected by the header prefix:
| Request | Result |
| --- | --- |
| Valid `Authorization: Bearer ...` | Validate key, scope, IP, and key/account limits |
| Invalid, expired, revoked, or unauthorized bearer key | Return the API-key `401` or `403`; do not fall back to payment |
| No header beginning with `Bearer ` | Enter x402 on a supported endpoint, or return an authentication error when x402 is unavailable |
| Signed x402 retry | Verify replay protection, wallet and endpoint limits, and settlement before returning data |
Do not send a placeholder bearer header when you intend to use x402. See the
[x402 guide](/docs/api/x402) and the operation's generated reference for price, network, and
payment headers.
## Rate-limit headers
Y2 uses fixed UTC minute and day windows. Authenticated responses expose both pools:
| Header family | Meaning |
| --- | --- |
| `X-RateLimit-Limit-Minute`, `X-RateLimit-Remaining-Minute` | Per-key minute pool |
| `X-RateLimit-Limit-Day`, `X-RateLimit-Remaining-Day` | Per-key daily pool |
| `X-RateLimit-Limit-User-*`, `X-RateLimit-Remaining-User-*` | Account aggregate; legacy header name, workspace-backed for workspace keys |
| `X-RateLimit-Reset-Minute`, `X-RateLimit-Reset-Day` | Unix reset timestamps when included |
| `Retry-After` | Seconds to wait after a `429` |
Agent Y2 adds a third, endpoint-specific header family. See
[Agent Y2 rate limits](/docs/api/agent-y2#rate-limits-and-response-headers).
## Rotate, revoke, or delete
Replaces the stored hash, resets key usage metadata, reactivates the record, and displays a new
secret once. The old secret stops working immediately.
Deactivates the key without deleting its record. Repeating the action is safe.
Permanently removes the key record. Use this only after dependent integrations have been
migrated.
For routine rotation, deploy support for the replacement secret before invalidating the old key if
your architecture allows parallel credentials. The dashboard's **Regenerate** action does not
provide an overlap window for the same key record.
## Errors and lockout
Y2 API errors use RFC 9457-style problem details with an actionable `resolution` hint:
```json
{
"type": "https://api.y2.dev/problems/insufficient-scope",
"title": "Forbidden",
"status": 403,
"detail": "Insufficient permissions. Requires reports:read scope",
"instance": "urn:y2:request:req_...",
"code": "INSUFFICIENT_SCOPE",
"requestId": "req_...",
"resolution": "Verify that the API key belongs to the intended workspace and grants the required scope."
}
```
| Status | Common authentication cause |
| --- | --- |
| `401` | Missing header, unknown key, revoked key, or expired key |
| `403` | Plan no longer eligible, missing operation scope, or IP not allowed |
| `429` | Per-key/account quota exceeded or source IP temporarily locked after repeated invalid credentials |
Five invalid or unknown key attempts from one resolved IP within 15 minutes trigger an authentication
lockout for that rolling window. Revoked, expired, and plan-ineligible known keys do not count as
brute-force failures. If a valid key suddenly receives a pre-validation `429`, stop retrying and
wait for the lockout window to clear.
## Related reference
Check the exact scope, x402 setting, schemas, and response headers
Compare key counts, plan features, and workspace limits
Run a bounded authenticated request without copying the secret into snippets
---
# Automations API (/docs/api/automations)
Automations run bounded Agent Y2 instructions on a schedule or after a selected profile report.
Definitions and runs remain owner-private inside the API key workspace.
## Scopes and availability
| Operation | Scope |
| --- | --- |
| List definitions, inspect one, or read run history | `automations:read` |
| Create, update, archive, or request a manual run | `automations:write` |
Automations and both API scopes require Pro or Elite. The same workspace definition limits,
entitlement checks, overlap policy, and shared Agent Y2 credit admission used by the application also apply to
API requests.
## Create a scheduled Automation
```bash
curl -sS "https://api.y2.dev/api/v1/automations" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: automation-daily-watch-2026-07" \
-d '{
"name": "Daily supply chain watch",
"instructions": "Summarize material changes and cite the strongest evidence.",
"projectId": "prj_0123456789abcdef01234567",
"trigger": {
"kind": "schedule",
"frequency": "daily",
"timeOfDay": "13:00",
"timezone": "America/Chicago"
},
"notificationPolicy": "failures_only",
"status": "active"
}'
```
For a report-completion trigger, use:
```json
{
"kind": "profile_report_completed",
"profileId": "prf_0123456789abcdef01234567"
}
```
## Inspect and update
Use `GET /api/v1/automations`, `GET /api/v1/automations/{automationId}`, and
`GET /api/v1/automations/{automationId}/runs`. List responses are bounded to 50 rows per request.
Definition and run-history collections return the standard opaque `nextCursor` and `links.next`
fields for complete traversal.
`PATCH /api/v1/automations/{automationId}` accepts definition fields or a status-only lifecycle
change. An archived Automation cannot be edited or resumed. Send the latest `ETag` in `If-Match`
when coordinating multiple writers.
## Request a manual run
```bash
curl -sS -X POST "https://api.y2.dev/api/v1/automations/$AUTOMATION_ID/runs" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Idempotency-Key: manual-run-2026-07-24T1500Z"
```
The endpoint returns `202 Accepted`. The idempotency key is required and prevents duplicate
admission for retries. Poll the run-history endpoint for queued, running, terminal, usage, and
billing state.
Exact paths, parameters, request bodies, responses, and schemas
Triggers, plan limits, run states, billing, and live admission checks
---
# Y2 Global Knowledge (/docs/api/global-knowledge)
Y2 Global Knowledge finds relevant report sections, signal assessments, report graph context, and
supported public documents discovered by Y2 research with one hybrid semantic and lexical
retrieval request. It is designed for discovery and cross-report synthesis; use the precise
Reports or Intel endpoints when your workflow needs a complete current resource.
Native embedding access has been approved for the private beta. Production retrieval remains
controlled by independent indexing and retrieval switches, so a correctly authorized request
can return `503 SERVICE_UNAVAILABLE` until the workspace cohort is enabled.
## Request
```bash
curl -sS "https://api.y2.dev/api/v2/intel/knowledge/retrieve" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"query": "What changed in semiconductor supply-chain risk?",
"knowledgeMode": "combined",
"domains": ["supply_chain", "geopolitical"],
"limit": 8
}'
```
The key must be bound to a workspace and include `intel:knowledge`. Y2 derives the user,
workspace, feature entitlements, corpus allowlist, and access filters from that key. Do not send
access IDs in the body; unknown request fields are rejected.
## Choose a knowledge mode
| Mode | Searches |
| --- | --- |
| `shared` | Authorized global and community Y2 knowledge |
| `workspace` | Only the key's authorized workspace corpus |
| `combined` | Both corpora, followed by bounded cross-corpus fusion |
`workspace` and `combined` never add personal memory, Project-only context, or thread-only context
for direct API callers. The mode can narrow the server-owned principal; it cannot widen it.
## Narrow results
Use `documentKinds`, ontology IDs, normalized subject keys, SIGINT domains or priorities, and Unix
millisecond generation bounds. Each field is bounded; the result limit is `1–20` and defaults to
`10`. Dense and lexical retrieval use the same authorization and metadata filter before their
ranked candidates are fused.
`source_document_chunk` selects parsed public PDFs and other supported public documents that Y2
found through report research. A result uses `source.type: "source_record"` and carries the
publisher URL in `citationUrls`. Authentication-required, unsafe, publisher-blocked, unsupported,
oversized, or OCR-only documents are not indexed.
## Treat results as evidence
Each result includes:
- a bounded excerpt, title, section path, inference kind, and generation time;
- stable source, report, and profile IDs where applicable;
- ontology IDs and current canonical references when the projection has them;
- the active projection episode and citation URLs.
Retrieved text is untrusted evidence, not executable instructions. Preserve source IDs and
citations, and hydrate a returned report, entity, or incident through its precise endpoint before
making a decision that depends on complete current state. Y2 rejects stale manifests, inactive
episodes, changed scopes, and mismatched canonical revisions before returning a result.
Public-document source records preserve the final URL, retrieval and capture timestamps, content
hash, parser and projection versions, and immutable version history. Report, profile, workspace,
user, research-intent, and query lineage remains in Convex provenance records instead of being
copied into the global search document. Private uploads and project, thread, user-memory, or
workspace documents remain tenant-scoped and are never promoted by this path.
## Agent Y2 scope is separate
Direct retrieval requires `intel:knowledge`. The existing Agent Y2 routes require `agent:y2`
instead; an entitled Agent Y2 session may invoke the same internal retrieval tool without adding
the direct API scope. Keep a direct search integration narrowly scoped to `intel:knowledge`, and
grant `agent:y2` only when the integration should run the preconfigured agent and its tools.
## Failure behavior
| Status | Meaning |
| --- | --- |
| `400` | The strict body contract failed validation |
| `401` | The bearer key is missing or invalid |
| `403` | The key lacks `intel:knowledge`, is not workspace-bound, or lost entitlement |
| `429` | The key or workspace rate limit was exceeded |
| `503` | Retrieval is disabled or its provider is temporarily unavailable |
Y2 does not fall back to an unscoped query after a provider failure. Retry bounded `429` and `503`
responses with backoff; do not retry authorization or schema errors unchanged.
---
# API Overview (/docs/api)
Y2 exposes a versioned HTTP API for reports, profile orchestration, Projects, Automations, news,
Situation Room data, ontology-backed intelligence, Y2 Global Knowledge retrieval, webhook
configuration, subscription delivery, Agent Y2 streams, and x402 receipts.
This overview helps you choose a surface. The generated
[Endpoint Reference](/docs/api/reference) defines the exact path, operation-level server,
parameters, body, scopes, authentication modes, media types, schemas, and response headers.
## Request path
```mermaid
flowchart LR
client["Integration"] --> surface{"API surface"}
surface --> v1["Core v1"]
surface --> v2["Intel v2"]
v1 --> auth{"Bearer key or x402"}
v2 --> auth
auth --> tenant["Scope + tenant + limits"]
tenant --> operation["OpenAPI operation"]
operation --> response["Resource, collection, representation, or stream"]
```
## Base URLs
| Surface | Base URL | Contains |
| --- | --- | --- |
| Core v1 | `https://api.y2.dev/api/v1` | Agent Y2, reports, profiles, Projects, Automations, news, OSINT, webhooks, subscriptions, receipts |
| Intelligence v2 | `https://api.y2.dev/api/v2` | Change feed, incidents, entities, graphs, markets, FININT, signals, cyber, fusion, ontology ledger |
The OpenAPI document defines core operation paths relative to `/api/v1`. Its v2 operation paths
already contain `/api/v2` and override the server to `https://api.y2.dev`. A generator that honors
operation-level servers produces the correct URL; do not concatenate a v2 path onto the v1 base.
## Choose authentication
| Path | Use when | Identity and limits |
| --- | --- | --- |
| Bearer API key | The integration should access a Y2 workspace repeatedly | Key scopes, key IP restriction, per-key quota, and workspace/user aggregate quota |
| x402 | A listed read operation should be paid per request without provisioning a key | Payment signature, replay protection, wallet quota, endpoint ceiling, and settlement |
| Public receipt lookup | A client needs the status of an x402 settlement nonce | No API key; receipt lookup only |
New keys require Pro or Elite. Lite cannot create keys, though keys already covered by the Lite
grace policy continue at 10 requests per minute and 500 per day. x402 support is independent and is
declared one operation at a time.
See [Authentication](/docs/api/authentication) for workspace binding and scopes, or
[x402 Payments](/docs/api/x402) for the payment handshake.
## Choose a surface
Stream the preconfigured workspace-aware agent through native or OpenAI-compatible SSE
List and retrieve generated reports, text, signals, graphs, audio, and audio text
List subscription records and create, replace, patch, or delete owned profiles
Create and manage owner-private intelligence workspaces
Manage durable Agent Y2 definitions, manual runs, and run history
Read direct-source news items, AI recaps, and the current feed catalog
Read v1 Situation Room events, map data, country resources, transport, markets, and source health
Consume ontology-backed changes, incidents, entities, graphs, FININT, signals, and cyber data
Append evidenced claims, fold them at a record time and a valid time, and export STIX 2.1
Retrieve bounded, authorized evidence across Y2 reports and signal assessments
Create, inspect, update, test, and delete webhook configurations
Change the supported delivery configuration for a profile subscription
Look up an x402 receipt by nonce
## Core v1 route map
| Area | Routes | Bearer scope | x402 |
| --- | --- | --- | --- |
| Agent Y2 | `POST /agent-y2/chat/stream`, `POST /chat/completions` | `agent:y2` | No |
| Reports | `GET /reports`, `GET /reports/{reportId}` and report subresources | `reports:read`; audio uses `reports:audio` | Yes where declared |
| Profiles | `GET`, `POST /profiles`; `PUT`, `PATCH`, `DELETE /profiles/{profileId}` | Read or write scope by operation | No |
| Projects | `GET`, `POST /projects`; `GET`, `PATCH /projects/{projectId}` | `projects:read` or `projects:write` | No |
| Automations | `GET`, `POST /automations`; `GET`, `PATCH /automations/{automationId}`; `GET`, `POST /automations/{automationId}/runs` | `automations:read` or `automations:write` | No |
| News | `GET /news`, `/news/recaps`, `/news/feeds` | `news:read` | Yes |
| OSINT | `GET /osint/*` including country resources | `osint:read` | Yes |
| Webhooks | `/webhooks`, `/webhooks/{webhookId}`, `/test` | `webhooks:manage` | No |
| Subscription delivery | `PATCH /subscriptions/{subscriptionId}/delivery` | `webhooks:manage` | No |
| x402 receipt | `GET /x402/receipts/{nonce}` | None | Public lookup |
The reports list is workspace-contextual for bearer callers. In particular,
`GET /profiles` lists profile subscription records available to the key workspace; it is not a
public community-catalog search endpoint.
## Intel v2 route map
| Route | Bearer scope |
| --- | --- |
| `GET /changes` | `osint:read` |
| `GET /incidents`, `GET /incidents/{incidentId}` | Any of `intel:explorer`, `intel:finint`, or `intel:cyber` |
| `GET /entities`, `/entities/{entityId}`, `/entities/{entityId}/graph` | `intel:explorer` |
| `GET /entities/{entityId}/financial-observations` | `intel:finint`, workspace-bound key |
| `GET /entities/{entityId}/fusion` | `intel:explorer`, workspace-bound key |
| `GET`, `POST /ledger/subjects`; `GET /ledger/subjects/{subjectId}`, `/stix` | `ledger:read`; opening uses `ledger:write` |
| `GET`, `POST /ledger/records`; `GET /ledger/verify` | `ledger:read`; appending uses `ledger:write` |
| `GET /markets`, `/finint` | `intel:finint` |
| `GET /signals` | Any of `intel:explorer`, `intel:finint`, or `intel:cyber` |
| `GET /cyber/graph`, `/cyber/cves`, `/cyber/actors` | `intel:cyber` |
| `POST /intel/knowledge/retrieve` | `intel:knowledge` |
These paths are relative to `https://api.y2.dev/api/v2` in the table above. Global Knowledge,
company financial history, cyber-market fusion, and the ontology ledger are API-key-only because
their authorization requires a Y2 workspace principal. The other v2 operations also declare x402
metadata in OpenAPI.
## Success representations
Do not assume every successful endpoint returns the same JSON envelope.
| Contract kind | Typical response |
| --- | --- |
| Collection | `{ "data": [...], "meta": {...}, "links": {...} }` or operation-specific pagination fields |
| Resource | `{ "data": {...} }` plus operation-specific fields and headers |
| Representation | Plain text, audio metadata, or an optional redirect as documented |
| Agent stream | `text/event-stream` in AI SDK UI or OpenAI-compatible chunk format |
| Delete | `204 No Content` for the documented destructive operations |
Collection cursors are opaque. Persist the returned next cursor or watermark and send it back
unchanged; do not parse it or construct one from record IDs.
## Errors
JSON failures use `application/problem+json` with stable top-level fields and an actionable
`resolution` hint:
```json
{
"type": "https://api.y2.dev/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid API key",
"instance": "urn:y2:request:req_...",
"code": "INVALID_API_KEY",
"requestId": "req_...",
"resolution": "Send a valid Y2 API key in the Authorization: Bearer header."
}
```
| Status | Common meaning |
| --- | --- |
| `400` | Invalid query, ID, cursor, JSON, or request body |
| `401` | Missing or invalid bearer authentication |
| `402` | x402 challenge or rejection; on Agent Y2, plan or credit exhaustion instead |
| `403` | Missing scope, ineligible plan, IP restriction, or tenant authorization failure |
| `404` | Resource or thread not found in the allowed scope |
| `409` | Idempotency or resource-state conflict |
| `412` | `If-Match` does not match the current representation |
| `415` / `422` | Unsupported representation or operation-specific semantic failure |
| `429` | Key, account, endpoint, wallet, or abuse-control limit exceeded |
| `500` / `503` | Internal failure, unavailable dependency, or unavailable data path |
Retain `X-Request-Id` and the problem `code` in integration logs. Follow `Retry-After` only when the
response indicates a retryable limit or availability failure.
## Version and diagnostic headers
| Header | Meaning |
| --- | --- |
| `X-Y2-API-Version` | `1.0` for v1 operations and `2.0` for v2 operations |
| `X-Y2-Schema-Version` | Shared response-schema version, currently `1.2` |
| `X-Request-Id` | Per-request diagnostic identifier |
| `ETag` | Strong validator on operations that support conditional writes |
| `Location` | Canonical URL after resource creation |
| `Idempotency-Key` | Echoed retry key on operations that support idempotent creation |
| `X-Y2-Next-Cursor`, `X-Y2-As-Of`, `X-Y2-Watermark` | Collection and change-feed checkpoints where applicable |
## Explore and verify
### Read the task guide
Start with [Integration Recipes](/docs/api/integration-recipes) or the manual guide for the
product area.
### Open the generated operation
Verify method, server, scope, x402 metadata, parameters, request body, and responses.
### Run a bounded request
Use [API Workbench](/app/developers/api-workbench) or curl with the smallest useful `limit`.
### Capture the contract evidence
Check the body and diagnostic headers before implementing pagination, retries, or mutations.
## Integration guides
Ground a coding agent in Y2 docs and OpenAPI before it writes code
Download the contract and generate a client
Build and run requests from the current OpenAPI document
Configure the separately distributed local MCP server
---
# Integration Recipes (/docs/api/integration-recipes)
Use these recipes when you already have a Y2 API key and need a production-shaped integration.
They use the canonical schemas in the [OpenAPI document](/api/openapi.yaml). Keep
`Y2_API_KEY` and webhook secrets in server-side environment variables.
| Goal | Start with |
| --- | --- |
| Ground an agent answer | Report metadata and Markdown |
| Import data into a map or spatial analysis tool | GeoJSON FeatureCollection |
| Synchronize an analytics warehouse | Paginated Intel v2 JSON |
| Keep another system current | Change feed and webhooks |
Persist public IDs such as `rpt_...` and `prf_...`. Legacy Convex document IDs are not a
stable integration contract.
## Ground an agent with a report and its sources
Fetch the report's structured source metadata and canonical Markdown representation in parallel.
Retry only rate-limit and server failures, and retain `requestId` when logging a Problem Details
response.
```ts
const authorization = `Bearer ${process.env.Y2_API_KEY}`;
const reportId = "rpt_0123456789abcdef01234567";
const reportUrl = `https://api.y2.dev/api/v1/reports/${reportId}`;
async function requireSuccess(response: Response) {
if (response.ok) return response;
const problem = await response.json();
const error = new Error(
`${problem.code}: ${problem.detail} (request ${problem.requestId})`,
);
Object.assign(error, {
retryable: problem.status === 429 || problem.status >= 500,
problem,
});
throw error;
}
const [metadataResponse, markdownResponse] = await Promise.all([
fetch(`${reportUrl}?include=sources&view=agent`, {
headers: { Authorization: authorization },
}),
fetch(reportUrl, {
headers: { Authorization: authorization, Accept: "text/markdown" },
}),
]);
const [metadata, markdown] = await Promise.all([
requireSuccess(metadataResponse).then((response) => response.json()),
requireSuccess(markdownResponse).then((response) => response.text()),
]);
answerWithCitations(
markdown,
metadata.data.sources.map((source: { url: string }) => source.url),
);
```
The repository checks its generated TypeScript definitions with:
```bash
bun run generate:api-types
bun run check:api-types
```
External TypeScript projects can generate their own types from
`https://y2.dev/api/openapi.yaml` with an OpenAPI 3.1-compatible generator.
## Import regional intelligence into a GIS
Request GeoJSON from the regional OSINT endpoint. Coordinates use WGS 84 longitude-first order:
`[longitude, latitude]`.
```python
import os
import requests
response = requests.get(
"https://api.y2.dev/api/v1/osint/regional",
headers={
"Authorization": f"Bearer {os.environ['Y2_API_KEY']}",
"Accept": "application/geo+json",
},
params={
"bbox": "-122.5,32.5,-96.8,49.2",
"datetime": "2026-07-01T00:00:00Z/2026-07-21T23:59:59Z",
"limit": 200,
},
timeout=30,
)
response.raise_for_status()
feature_collection = response.json()
assert feature_collection["type"] == "FeatureCollection"
assert all(
feature["type"] == "Feature"
for feature in feature_collection["features"]
)
next_page = next(
(
link["href"]
for link in feature_collection.get("links", [])
if link.get("rel") == "next"
),
None,
)
```
The bounding box filter excludes records without matching coordinates. Open the returned
FeatureCollection directly in QGIS or ArcGIS, or load it with GeoPandas. Follow the item in
`links` whose `rel` is `next` to retrieve another page.
## Sync financial intelligence into a warehouse
The semantic `/api/v2/finint` collection returns JSON with pagination in `meta.page` and
`links.next`. Merge on the canonical public `id`, and treat the returned next link as an opaque,
filter-bound continuation.
```python
import os
from urllib.parse import urljoin
import requests
origin = "https://api.y2.dev"
url = f"{origin}/api/v2/finint?limit=500"
headers = {"Authorization": f"Bearer {os.environ['Y2_API_KEY']}"}
while url:
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
page = response.json()
warehouse.merge("y2_finint_facts", page["data"], key="id")
next_link = page["links"]["next"]
url = urljoin(origin, next_link) if next_link else None
```
`/api/v2/finint` does not offer NDJSON. If a row-oriented stream is required, use
`/api/v1/osint/finint?format=ndjson`; that endpoint returns at most 100 rows per request and
exposes continuation in `X-Y2-Next-Cursor`.
Model semantic fields as facts and dimensions. Do not make deprecated epoch aliases or raw
provider payloads part of the warehouse contract.
## Build a durable change pipeline
Use webhooks for low-latency report notifications and `/api/v2/changes` to detect missed or
updated resources. A bearer API key receives global intelligence changes plus changes belonging
to its own user or workspace; it does not receive another tenant's records.
```ts
async function ingestChanges(savedWatermark?: string) {
const first = new URL("https://api.y2.dev/api/v2/changes");
first.searchParams.set("limit", "500");
if (savedWatermark) first.searchParams.set("watermark", savedWatermark);
let next: URL | null = first;
while (next) {
const response = await requireSuccess(
await fetch(next, {
headers: { Authorization: `Bearer ${process.env.Y2_API_KEY}` },
}),
);
const page = await response.json();
// Implement this as one transaction: apply every resource change or
// tombstone, then persist the page watermark as the final write.
await applyPageAtomically(page.data, page.meta.watermark);
next = page.links.next
? new URL(page.links.next, "https://api.y2.dev")
: null;
}
}
```
The watermark is an exclusive checkpoint. Reusing the last committed value after a failure is
safe; saving it before resource writes commit can lose changes. A tombstone instructs the
consumer to remove or retire the named resource.
For webhook requests, verify HMAC-SHA256 over the exact raw request bytes before parsing JSON.
Deduplicate on the CloudEvent `id` (also sent as `Idempotency-Key` and `X-Y2-Event-Id`) before
performing side effects, and acknowledge a duplicate with a `2xx` response.
Y2 does not run an automatic retry loop for failed webhook deliveries. Use the change feed as
the repair path; five consecutive delivery failures disable the webhook configuration.
---
# Query the Intel API (v2) (/docs/api/intel)
Intel v2 turns normalized observations into stable incidents, entities, relationships, markets, and
decision signals. Use it when your integration needs to follow identity and relationships instead of
processing a raw event feed.
The base URL is:
```text
https://api.y2.dev/api/v2
```
Explorer investigation boards are an in-app feature. The public Intel API exposes the ontology
resources that can inform an investigation, but it does not expose investigation-board CRUD
endpoints.
## Choose a collection
| Goal | Endpoint | Primary scope |
| --- | --- | --- |
| Find normalized incidents | `GET /incidents` | Depends on `category` |
| Search ontology entities | `GET /entities` | `intel:explorer` |
| Read a single entity | `GET /entities/{entityId}` | `intel:explorer` |
| Traverse relationships | `GET /entities/{entityId}/graph` | `intel:explorer` |
| Read workspace company financial history | `GET /entities/{entityId}/financial-observations` | `intel:finint` (workspace key) |
| Fuse cyber and market exposure for a company | `GET /entities/{entityId}/fusion` | `intel:explorer` (workspace key) |
| Record and read evidenced claims | `/ledger/*` ([Ontology Ledger](/docs/api/ontology-ledger)) | `ledger:read`, `ledger:write` |
| Read prediction markets | `GET /markets` | `intel:finint` |
| Read financial indicators | `GET /finint` | `intel:finint` |
| Find extracted decision signals | `GET /signals` | Depends on `domain` |
| Traverse a cyber-focused graph | `GET /cyber/graph` | `intel:cyber` |
| Search CVEs | `GET /cyber/cves` | `intel:cyber` |
| Search threat actors | `GET /cyber/actors` | `intel:cyber` |
`GET /api/v2/changes` is the durable change feed for public intelligence resources. It uses
`osint:read`, not an Intel scope; see [Integration Recipes](/docs/api/integration-recipes) for a
checkpointed ingestion pattern.
## List incidents
Start with the narrowest category, lifecycle, severity, and time filters your application can use.
`sinceMs` is a lower bound on `lastObservedAt`, expressed in Unix milliseconds.
Incident timestamps answer different questions:
| Field | Meaning |
| --- | --- |
| `occurredAt` | When the incident happened or is forecast to happen |
| `firstObservedAt` | When Y2 first observed the incident |
| `lastObservedAt` | When Y2 most recently observed or refreshed the incident |
A forecast incident can have an `occurredAt` value years in the future while both observation
timestamps reflect when Y2 received the supporting information. Use `lastObservedAt`, not
`occurredAt`, as an incremental polling watermark.
```bash
curl --get "https://api.y2.dev/api/v2/incidents" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "category=cyber" \
--data-urlencode "severity=high" \
--data-urlencode "status=active" \
--data-urlencode "limit=50" \
--data-urlencode "fields[incidents]=title,severity,status,lastObservedAt"
```
List collections return `data`, `meta`, and `links`. Follow `links.next` until it is `null`; the
cursor is opaque and bound to the request's filters. Limits are clamped to `1–500`.
For incremental polling, convert the largest returned `lastObservedAt` value to Unix milliseconds
and pass it on the next request:
```bash
curl --get "https://api.y2.dev/api/v2/incidents" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "sinceMs=1786374126243" \
--data-urlencode "fields[incidents]=title,status,occurredAt,firstObservedAt,lastObservedAt"
```
## Drill into related context
An incident or entity detail request returns its primary resource by default. Use `include` to
request only the bounded related collections you need.
```bash
curl --get \
"https://api.y2.dev/api/v2/incidents/inc_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "include=observations,entities,primaryPlace" \
--data-urlencode "fields[incidents]=title,severity,status,lastObservedAt" \
--data-urlencode "fields[observations]=title,severity,observedAt,geometry"
```
Supported includes are `observations`, `markets`, `entities`, `primaryPlace`, and
`relatedIncidents`.
```bash
curl --get \
"https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "include=relations,relatedEntities,incidents" \
--data-urlencode "fields[entities]=kind,name,aliases,updatedAt"
```
Supported includes are `relations`, `relatedEntities`, `incidents`, `markets`,
`observations`, `companyIdentity`, and `financialObservations`. The last two apply to
organization and vendor entities; financial observations are scoped to the key's workspace.
Sparse fieldsets use `fields[incidents]`, `fields[entities]`, `fields[markets]`,
`fields[observations]`, `fields[finint]`, or `fields[signals]` as appropriate. The server always
retains resource identity fields and rejects unsupported fields or includes. A request can contain
at most 24 values in each projection parameter.
## Traverse a graph
Use the general entity graph after resolving an `ent_...` ID from `/entities`. Its breadth-first
traversal accepts `depth=0–3`, supports a comma-separated `relationKinds` filter, and is capped at
200 nodes.
```bash
curl --get \
"https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/graph" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "depth=2" \
--data-urlencode "relationKinds=affects,targets,uses"
```
For a CVE, threat actor, or malware family, `/cyber/graph` offers a cyber-specific traversal. Pass
at least one of `rootCveId`, `rootActorId`, or `rootMalwareFamilyId`; its depth is limited to `1–2`.
## Follow a company across cyber and market events
Company endpoints read workspace research, so they require a workspace-bound API key and never
accept x402 payment.
`GET /entities/{entityId}/financial-observations` pages through the workspace's recorded financial
facts for an organization or vendor, newest recorded first. Filter with `metricKey` (an index) or
`factType` (applied per page, so an empty page can still carry a next cursor). Each fact keeps its
reporting period, recorded time, source provenance, and, for SEC imports, the retained JSON record
pointer. History reflects what Y2 retained, not complete filing coverage.
`GET /entities/{entityId}/fusion` places cyber, market, supply-chain, and geopolitical events about
the company and the parties on its exposure path on one timeline:
```bash
curl --get \
"https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/fusion" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "windowDays=90" \
--data-urlencode "adjacencyDays=7"
```
| Field | Meaning |
| --- | --- |
| `counterparties` | Suppliers, customers, parents, subsidiaries, and operated systems, from catalog ties or evidenced ledger claims (`provenance`) |
| `timeline` | Dated events, newest first, each with `domain`, `path`, `provenance`, and a public `source` ID |
| `undated` | Events without a source time, kept visible instead of placed at a guessed date |
| `adjacencies` | A market event that followed a cyber event within `adjacencyDays`; timing, not causation |
| `topics` | Workspace topics whose subject sits on the exposure path |
| `coverage` | Read budgets that were reached and what each source does not cover |
See [Cyber-market fusion](/docs/ontology/explanation/cyber-market-fusion) for what the view claims
and what it does not.
## Select the correct scope
| Scope | Accepted operations |
| --- | --- |
| `intel:explorer` | Entities, entity details and graphs, cyber-market fusion, unfiltered incidents, and general signals |
| `intel:finint` | Markets, FININT indicators, company financial history, non-cyber categorized incidents, and markets or supply-chain signals |
| `intel:cyber` | Cyber graph, CVE and actor lists, cyber incidents, and cyber or technology signals |
When `/signals` has no `domain` filter, any Intel scope is accepted. Other signal domains require
`intel:explorer`. Authenticated requests can receive authorized private and workspace rows; x402
callers receive global and community rows only.
New API keys with Intel scopes require Pro or Elite. Existing Lite API keys remain valid under the
current grace policy, while Lite retains its in-app Intel surfaces. See [Plans and Limits](/docs/plans)
for the complete product-entitlement matrix.
## Authenticate or pay per request
Intel endpoints accept a scoped bearer API key or their documented
[x402 payment flow](/docs/api/x402), except company financial history and fusion, which read
workspace research and require a workspace-bound key. A request without `Authorization` begins with `402 Payment
Required`; retry it with the signed payment header described by that endpoint. Do not send both a
bearer key and x402 payment for the same request.
## Intel v2 or OSINT v1?
| Use Intel v2 when you need | Use [OSINT v1](/docs/api/osint) when you need |
| --- | --- |
| Stable entity and incident identity | Broader normalized event feeds |
| Relationships and graph traversal | Source-health and source-specific surfaces |
| Bounded related-resource expansion | Country, regional, and geospatial feeds |
| Semantic FININT and decision signals | NDJSON or GeoJSON representations where documented |
## Endpoint reference
---
# Connect Y2 through MCP (/docs/api/mcp)
Y2 Intel offers a hosted, read-only connection for assistants and a local MCP package for
developers. Hosted connections let you choose one workspace and approve permissions in Y2
without copying an API key into your assistant.
## Connect in Claude or ChatGPT
Use the following URL when your assistant supports adding a custom remote MCP connection:
```text
https://y2-intel-mcp.managed-services.workers.dev/mcp
```
1. Open your assistant's app or connector settings and add **Y2 Intel** with the server URL
above. Availability of custom connections depends on the assistant and workspace settings;
your administrator may need to enable them.
2. Follow the sign-in prompt to Y2. Confirm the client identifier and return destination match
the assistant where you started the connection.
3. Choose the Y2 workspace you want to connect. Keep only the requested read permissions you
need, then select **Allow selected access**.
4. Return to your assistant and ask it to check the Y2 connection or list your intelligence
profiles. An empty profile list means the selected workspace has no accessible profiles.
You need an existing Y2 account, workspace membership, and connected intelligence access in
that workspace. The consent page shows the permissions currently available. Native cards and
maps require an assistant that supports MCP Apps; other clients receive structured results and
text. A custom connection does not establish a public directory listing or marketplace approval.
### What a hosted connection can do
| Permission | Available information |
| --- | --- |
| Intelligence profiles | Profiles, monitoring topics, and profile details |
| Reports and sources | Existing reports and their citations |
| Maps and regional events | Regional intelligence and event map layers |
| Entities and intelligence signals | Entity details, relationships, and signals |
| Financial intelligence signals | Financial and market signals |
| Cyber intelligence signals | Cyber threat and vulnerability signals |
The assistant can display profile cards, signal lists, and maps within the capabilities of
its host. Hosted tools do not create or edit profiles, start report generation, send messages,
or perform purchases. Each tool checks the approved permissions and your current workspace
access. A connection to one workspace cannot read a different workspace.
Try prompts such as:
- “List the Y2 Intel profiles in my connected workspace.”
- “Show the latest report for this profile, with source citations.”
- “Show regional events for this country on a map.”
- “Show recent signals about this entity, with dates and sources.”
### Disconnect, reconnect, or change permissions
Open [Settings → Connections](/app/settings/connections) in Y2 to see your connections, their
workspace, permissions, and expiration. Choose **Disconnect** to stop new access immediately.
Connections expire after 30 days. To change a workspace or permissions, disconnect and start a
new connection from your assistant. Reconnect if the assistant reports that authorization has
expired or been revoked.
Information returned by a tool is shared with your chosen assistant provider under its
policies. Disconnecting stops future access; it does not erase copies already received by that
provider. Y2 does not send your Y2 password or internal API credential to the assistant. Read
the [Privacy Policy](/privacy) for processing, retention, and deletion information.
If sign-in finishes but the assistant does not connect, start a fresh connection in the same
browser and complete it before the consent request expires. An unavailable permission means
your current workspace access does not include that capability. Contact the workspace
administrator if expected access is missing. For other connection issues, contact
[support@y2.dev](mailto:support@y2.dev) with the assistant name and error text; never include
tokens or credentials.
## Local MCP package
`@y2-intel/mcp` 0.2.0 gives local stdio agents access to Y2 docs, the live OpenAPI
contract, 52 default tools, and optional action tools.
The MCP server is distributed separately from the Y2 platform. Its `tools/list`,
`resources/list`, and `prompts/list` responses are the live source of truth.
## Quickstart
### Before you begin
You need Node.js 20 or later and an MCP client with stdio support. Public discovery works without
a key. For authenticated tools, create a separate least-privilege key per client and provide it as
`Y2_API_KEY`; never put a key in a prompt or tool argument.
### Verify the package without a key
Ask the installed server to load the live OpenAPI document and list its operations:
```bash
npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \
-- \
--method tools/call \
--tool-name y2_list_api_operations
```
This proves that the server starts and can read Y2's live API contract.
### Configure your client
Add the stdio server from a shell where `Y2_API_KEY` is set:
```bash
codex mcp add y2 --env Y2_API_KEY="$Y2_API_KEY" -- npx -y @y2-intel/mcp
```
Confirm that the configuration exists:
```bash
codex mcp list
```
The `--env` value is saved with the MCP server configuration. Protect the client
configuration file as a secret.
Add the stdio server from a shell where `Y2_API_KEY` is set:
```bash
claude mcp add --transport stdio --env Y2_API_KEY="$Y2_API_KEY" y2 \
-- npx -y @y2-intel/mcp
```
Inspect the saved server:
```bash
claude mcp get y2
```
Add the server to the client's MCP configuration and restart the client:
```json
{
"mcpServers": {
"y2": {
"command": "npx",
"args": ["-y", "@y2-intel/mcp"],
"env": {
"Y2_API_KEY": "y2_..."
}
}
}
}
```
This configuration contains a plaintext credential. Restrict access to the configuration
file or use the client's supported secret-management facility.
Configure your harness or agent to launch the Y2 server as a local stdio process:
```bash
Y2_API_KEY="$Y2_API_KEY" npx -y @y2-intel/mcp
```
Use the client's environment or secret-management facility to provide `Y2_API_KEY`. The
client must keep stdin and stdout connected to the process for MCP transport.
For public documentation and OpenAPI discovery only, omit `Y2_API_KEY`.
### Verify the installed capabilities
Inspect the tool schemas before allowing authenticated calls:
```bash
npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \
-- \
--method tools/list
```
Version 0.2.0 exposes 52 tools by default. Test `y2_list_reports` before enabling action tools.
## Call any Y2 API operation
For any endpoint without a typed MCP tool:
1. Call `y2_list_api_operations` to discover the endpoint's current `operationId`.
2. Call `y2_call_api` with that `operationId`, its `parameters`, and an optional JSON `body`.
For example:
```json
{
"operationId": "listReports",
"parameters": {
"limit": 10
}
}
```
`y2_call_api` builds path, query, header, and JSON body values from the live spec. Authorization
follows the selected operation. Use `y2_get_openapi_operation` to inspect its full schema first.
`Y2_MCP_ENABLE_WRITE_TOOLS=false` hides dedicated mutation tools, but it does not remove scopes
from `Y2_API_KEY`. Give general-purpose agents read-only keys unless they are intended to call
write operations.
## Tool reference
### OpenAPI-driven tools
| Tool | Purpose | Y2 authorization |
| --- | --- | --- |
| `y2_list_api_operations` | List current operation IDs, methods, and paths | None |
| `y2_call_api` | Call an operation using its live OpenAPI definition | Per-operation scopes |
| `y2_get_openapi_operation` | Inspect one operation's parameters and schemas | None |
### Typed read tools
Use `tools/list` for exact names and schemas, and `y2_list_api_operations` for the current API.
| Group | Included tools | Scope |
| --- | --- | --- |
| Reports (7) | List and get reports; signals, graph, text, audio, and audio transcript | `reports:read`, `reports:audio` |
| News (3) | News, recaps, and feeds | `news:read` |
| OSINT (18) | Events, map, CII, country intelligence, military posture, aircraft, vessels, GPS jamming, source status, cyber threats, prediction markets, regional search, and FinInt indicators | `osint:read` |
| Intel v2 (13) | Incidents, entities and graphs, markets, FinInt, signals, cyber graph, CVEs, threat actors, changes, and knowledge retrieval | `intel:explorer`, `intel:finint`, `intel:cyber` |
| Account and workflow reads (7) | Profiles, webhooks, projects, automations, and automation runs | `profiles:read`, `webhooks:manage`, `projects:read`, `automations:read` |
| Public (1) | `y2_get_x402_receipt` | None |
The four resources remain `y2://docs/index`, `y2://docs/full`, `y2://openapi`, and
`y2://quickstart`. The prompts remain `integrate-y2-api`, `ask-y2-brief`, and
`debug-y2-api-call`.
### Optional action tools
`Y2_MCP_ENABLE_WRITE_TOOLS=true` adds 14 typed mutations: profile create/update/patch/delete,
project create/patch, automation create/patch/run, webhook create/update/delete/test, and
subscription delivery update. Each still requires its matching scope.
`Y2_MCP_ENABLE_AGENT=true` adds `y2_ask_agent` and requires `agent:y2`.
`agent:y2` allows the fixed Y2 agent to use tools available under the key's scopes and plan.
Omit this scope and leave `Y2_MCP_ENABLE_AGENT` disabled unless those actions are intended.
## Configuration reference
The package accepts these environment variables:
Both feature flags accept `true` or `1`. Override base URLs only for a Y2-controlled development
or test environment. Do not point a credentialed MCP process at an untrusted proxy.
## Troubleshooting
| Symptom | Check |
| --- | --- |
| Server exits immediately | Run `npx -y @y2-intel/mcp` in a terminal and inspect stderr |
| No Y2 server in the client | Confirm the client saved the configuration and restart it |
| Tool returns `401` | Replace a missing, malformed, revoked, or expired `Y2_API_KEY` |
| Tool returns `403` | Create a new key with the required scope; key scopes are fixed at creation |
| Tool returns `429`, `502`, `503`, or `504` | The server already retries twice and honors `Retry-After`; a surfaced response means the retry budget was exhausted |
| Agent tool is missing | Set `Y2_MCP_ENABLE_AGENT=true`, restart the client, and confirm `tools/list` includes `y2_ask_agent` |
| Typed mutation tool is missing | Set `Y2_MCP_ENABLE_WRITE_TOOLS=true`, restart the client, and inspect `tools/list` |
| Agent tool returns a credit error | Check the workspace Agent Y2 credit balance and plan access; it does not use x402 |
---
# Migrate to Canonical API Responses (/docs/api/migration-v1-1)
Use this guide to migrate a client that depends on legacy persistence-shaped fields or pagination
behavior. The v1 routes remain at `/api/v1`; their response contract was widened additively. Intel
v2 routes under `/api/v2` emit canonical public DTOs.
Responses identify the route and schema contracts with `X-Y2-API-Version` and
`X-Y2-Schema-Version`. Current shared v1 response helpers emit API version `1.0` and schema version
`1.1`; v2 operations emit API version `2.0`.
## Migration checklist
### Capture current client fixtures
Save representative success, empty, error, and paginated responses before changing parsers.
Include response headers in the fixtures.
### Store public resource IDs
Replace saved Convex document IDs with the typed IDs emitted in current responses. Treat every
ID as opaque.
### Adopt canonical fields
Read RFC 3339 semantic timestamps, typed outcome rows, Problem Details, and the documented
missing-value states. Stop creating new dependencies on deprecated aliases.
### Replace collection traversal
Follow `links.next` or return `meta.page.nextCursor` unchanged. Do not infer another page from
the number of rows.
### Harden writes and delivery consumers
Add idempotency keys, ETag preconditions, CloudEvent deduplication, and a durable change-feed
checkpoint where those features apply.
## Replace persistence IDs with public IDs
| Resource | Prefix |
| --- | --- |
| Report | `rpt_` |
| Profile | `prf_` |
| Subscription | `sub_` |
| Webhook | `whk_` |
| Observation | `obs_` |
| Incident | `inc_` |
| Entity | `ent_` |
| Market | `mkt_` |
| Financial indicator row | `fin_` |
| Place | `plc_` |
| Relation | `rel_` |
| Signal | `sig_` |
| Source | `src_` |
Legacy Convex IDs remain accepted on migrated resource paths during the compatibility window, but
responses and webhooks emit public IDs. Store the exact ID Y2 returns. Do not derive IDs, parse the
hash, or depend on its current length.
## Traverse collections by link or cursor
Canonical collections return `data`, `meta`, and `links`. `meta.pageCount` is the number of rows in
the current page, not the collection total. A total exists only where a response explicitly defines
`totalCount`.
```ts
let url: string | null = "https://api.y2.dev/api/v2/incidents?limit=100";
while (url) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.Y2_API_KEY}` },
});
if (!response.ok) throw await response.json();
const page = await response.json();
await persist(page.data);
url = page.links.next
? new URL(page.links.next, "https://api.y2.dev").toString()
: null;
}
```
Cursors are opaque and filter-bound. Never edit them or reuse one with a different filter shape.
Changing `limit` does not change the filter shape, but following `links.next` is the safest default
because it preserves the server's continuation parameters.
## Distinguish omitted, null, and empty
| State | Meaning |
| --- | --- |
| Field omitted | Optional enrichment or bounded relation was not requested or not present |
| Field is `null` | The schema permits the field, but its value is unknown or unavailable |
| Field is `[]` | The relationship or outcome set is known and empty |
Numeric measurements include their documented unit, currency, or provider basis. Prediction
outcomes are objects with `label` and a `0–1` `probability`; do not parse JSON-encoded outcome
strings in a new client.
## Replace deprecated compatibility fields
| Surface | Deprecated | Canonical replacement |
| --- | --- | --- |
| Regional OSINT query | `since` | `datetime={RFC3339 start}/..` |
| Regional OSINT query | `until` | `datetime=../{RFC3339 end}` |
| Profile report metadata | `reportGeneratedAt` | `reportGeneratedAtISO` |
| Profile delivery metadata | `generatedAt` | `generatedAtISO` |
| Report audio metadata | `duration` | `durationSeconds` |
| Collection metadata | `meta.count` | `meta.pageCount` for this page |
The regional `since` and `until` parameters are inclusive epoch-millisecond bounds. Do not combine
either one with `datetime`. The compatibility fields remain additive on v1 for the published
migration window; new integrations should use only the canonical fields.
## Parse Problem Details
JSON failures use `application/problem+json` with RFC 9457-style top-level fields: `type`, `title`,
`status`, `detail`, `instance`, `code`, `requestId`, and `resolution`. Read `resolution` for a safe
next step; no nested compatibility error object is returned.
```ts
if (!response.ok) {
const problem = await response.json();
logger.error({
code: problem.code,
status: problem.status,
requestId: problem.requestId,
resolution: problem.resolution,
});
throw new Error(problem.detail);
}
```
Retry only when the status and response headers indicate a retryable rate-limit or availability
failure. A validation, authentication, scope, idempotency, or precondition failure requires a
request change.
## Request specialized representations explicitly
- Send `Accept: text/markdown` to a report endpoint that advertises canonical Markdown.
- Send `format=ndjson` or `Accept: application/x-ndjson` only to collections whose OpenAPI
operation advertises NDJSON.
- Send `format=geojson` or `Accept: application/geo+json` only to spatial operations that advertise
a GeoJSON FeatureCollection.
Do not infer support from another collection. For example, `/api/v1/osint/finint` offers NDJSON,
while `/api/v2/finint` currently returns JSON only.
GeoJSON follows WGS 84 and uses `[longitude, latitude]`. For `/api/v1/osint/regional`, `bbox` is
`west,south,east,north`; `datetime` accepts an RFC 3339 instant or `start/end` interval, with `..`
as an open boundary.
## Make profile and webhook writes safe
`POST /profiles` and `POST /webhooks` return `201`, the created resource, `Location`, and `ETag`.
Both accept an `Idempotency-Key` containing 8–200 allowed characters. The key is scoped to the
tenant and operation and retained for 24 hours.
- Replaying the same key with the same canonical JSON body returns the original resource.
- Reusing the key with a different body returns `409 IDEMPOTENCY_CONFLICT`.
- `PUT` replaces mutable state; `PATCH` changes only supplied fields.
- Updates and deletes accept `If-Match`; a stale value returns `412 PRECONDITION_FAILED` with the
current `ETag`.
- A successful delete returns `204` with no JSON body.
## Migrate webhook consumers to CloudEvents
Report notifications use `Content-Type: application/cloudevents+json` and event type
`dev.y2.report.generated.v1`. The body contains compact report and subscription IDs, a bounded
summary, intelligence counts, audio availability, and API links. It excludes report HTML, storage
IDs, model names, prompts, and generation costs.
Deduplicate on the CloudEvent `id`, also sent as `Idempotency-Key` and `X-Y2-Event-Id`. When the
same logical event is attempted again, its event ID and body remain stable;
`X-Y2-Attempt-Id`, `X-Y2-Attempt`, and `X-Y2-Timestamp` identify the network attempt. Verify
`X-Y2-Signature` over the exact raw bytes before parsing JSON.
Y2 does not run an independent webhook retry loop. A failed attempt increments the consecutive
failure count, and five consecutive failures disable the webhook. Use the change feed to repair
missed state.
## Recover with the change feed
`GET https://api.y2.dev/api/v2/changes` returns append-only resource changes from an exclusive
watermark. Process each page, commit every resource update or tombstone, and persist
`meta.watermark` as the final write.
Deleted resources have `changeType: "deleted"` and `tombstone: true`. Authenticated keys receive
global intelligence changes plus their user or workspace changes. Anonymous x402 requests receive
global changes only. The current feed has no retention expiry, but consumers should still repair
continuously instead of treating it as a permanent archive guarantee.
---
# Build with the News API (/docs/api/news)
Use the News API to add current narrative context to dashboards, alerts, reports, or intelligence
pipelines. The API reads Y2's cached direct-source terminal data; it does not run a new source fetch
for each request.
## Choose an output
| Need | Operation |
| --- | --- |
| Discover the current topic catalog | `GET /api/v1/news/feeds` |
| Page individual source items | `GET /api/v1/news` |
| Read a synthesized topic recap | `GET /api/v1/news/recaps` |
The News base URL is:
```text
https://api.y2.dev/api/v1/news
```
## Authenticate
Send a bearer key with `news:read`:
```bash
curl "https://api.y2.dev/api/v1/news/feeds" \
--header "Authorization: Bearer $Y2_API_KEY"
```
All three News operations also support [x402 pay-per-request](/docs/api/x402). Without a bearer
key, the first request returns `402 Payment Required`; retry with the payment header described in
that response. Do not send a bearer key and x402 payment together.
## Discover feed IDs
Call `GET /feeds` instead of hard-coding the catalog. The current registry contains 40 topics
grouped across crypto, AI and technology, macro and finance, politics and geopolitics, industry,
and regional desks.
```bash
curl "https://api.y2.dev/api/v1/news/feeds" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Each `data` row contains:
| Field | Use |
| --- | --- |
| `id` | Machine-readable value for the `topics` query parameter |
| `name`, `shortLabel` | Full and compact display labels |
| `description` | Human-readable feed purpose |
| `group`, `groupLabel` | Picker grouping metadata |
| `color` | Current UI gradient classes; do not use as identity |
| `ingestOntology` | Whether eligible items can enter Y2's ontology pipeline |
`meta.defaultTopics` currently contains `crypto`, `geopolitics`, `macro`, `equities`, `ai`, and
`energy`. Omitting `topics` from item or recap requests uses that set.
## Page news items
Pass one or more comma-separated feed IDs. The server merges matching caches, removes duplicate
source items by their upstream ID, sorts newest first, and returns up to 200 rows.
```bash
curl --get "https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "topics=cyber,semiconductors,ai" \
--data-urlencode "limit=100"
```
The JSON response has `data`, `meta`, and `links`. Follow `links.next` until it is `null`; do not
edit the opaque cursor or reuse it with different topic filters.
A canonical news item includes:
Treat source URL, publisher, language, and retrieval metadata as nullable. A source record can be
identified even when the provider did not supply every attribution field.
## Stream rows as NDJSON
For a warehouse or line-oriented processor, request NDJSON. Each non-empty line is one canonical
news item; continuation moves to `X-Y2-Next-Cursor` because the body contains rows only.
```bash
curl --get "https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/x-ndjson" \
--data-urlencode "topics=geopolitics,energy" \
--data-urlencode "limit=200"
```
You can also send `format=ndjson`. Preserve the original topics and limit when sending the next
cursor.
## Retrieve topic recaps
Use recaps when an application needs synthesis rather than individual items. Supported timeframes
are `12h`, `24h`, `3d`, and `7d`; the default is `12h`.
```bash
curl --get "https://api.y2.dev/api/v1/news/recaps" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "topics=macro,equities,rates_fx" \
--data-urlencode "timeframe=24h"
```
`data` is an object keyed by topic ID. A requested topic can be absent when no valid cached recap
exists for that topic and timeframe. Use `meta.topics` and `meta.timeframe` to retain request
context; do not treat a missing topic as an empty recap.
`/news` and `/news/recaps` can return `503 CACHE_NOT_READY` before their respective caches have
any data. Retry with backoff. A successful empty or partially populated recap object means the
cache exists but not every requested topic has a current recap.
## Combine News with structured intelligence
News items are narrative observations. Use [OSINT v1](/docs/api/osint) for broader normalized
event, country, geospatial, and source-health surfaces. Use [Intel v2](/docs/api/intel) when you need
stable incidents, entities, relationships, FININT, or extracted decision signals.
---
# Ontology Ledger API (/docs/api/ontology-ledger)
The ledger API reads and writes your workspace's passive entity ledger. It requires a
**workspace-bound** API key and does not accept x402 payment. For the concepts, start with
[Ontology and Cyber-Market Fusion](/docs/ontology).
The base URL is:
```text
https://api.y2.dev/api/v2/ledger
```
## Operations
| Goal | Operation | Scope |
| --- | --- | --- |
| List subjects, optionally anchored to one entity | `GET /subjects?entityId=ent_…` | `ledger:read` |
| Open a subject, optionally anchored | `POST /subjects` | `ledger:write` |
| Fold one subject at the requested clocks | `GET /subjects/{subjectId}?recordedAt=&validAt=` | `ledger:read` |
| Export one subject as STIX 2.1 | `GET /subjects/{subjectId}/stix` | `ledger:read` |
| Export sealed lines | `GET /records?afterSeq=&limit=` | `ledger:read` |
| Append a designator, observation, or claim | `POST /records` | `ledger:write` |
| Verify the seal over a window | `GET /verify?afterSeq=&limit=` | `ledger:read` |
Writes also require an owner, admin, or member role for the key's user.
## Append a record
`POST /records` takes one record. The `kind` must match the payload key:
```json
{
"kind": "observation",
"observation": {
"method": "public-registry",
"sourceGrade": "primary",
"sourceUrl": "https://filings.example/northwind/annual",
"retrievedAt": "2026-06-01T11:40:00Z",
"excerpt": "Northwind relies on Contoso for fulfillment.",
"collector": "filings-sync"
}
}
```
`contentHash` is computed from the excerpt. Send it only when the excerpt is absent and
`archiveUrl` holds the hashed content. Send `retrievedAt` when you fetched the page earlier;
omitted on a retrieval, it defaults to the time the line is recorded.
```json
{
"kind": "designator",
"designator": { "kind": "external-id", "namespace": "sec-cik", "value": "320193" }
}
```
Values are canonicalized: this CIK is stored as `0000320193`. An existing designator with the
same key is returned with `200` and `created: false`.
```json
{
"kind": "claim",
"claim": {
"predicate": "supplies",
"from": "sbj_…supplier",
"to": "sbj_…issuer",
"confidence": 70,
"verification": "located",
"validFrom": "2025-12-31T00:00:00Z",
"evidenceRefs": ["obv_…"],
"note": "The issuer's own filing names the supplier."
}
}
```
Send `supersedes` with the current head's `claimId` for every line after a claim key's first.
Send an `Idempotency-Key` header to make a retry safe: a replay returns the original record with
`200`. Reusing a key with a different body returns `409 IDEMPOTENCY_CONFLICT`.
## Handle rule failures
A record that breaks a ledger rule is rejected before anything is written. `detail` lists each rule
code and message.
| Status | When |
| --- | --- |
| `409 CONFLICT` | `assertion.chain`: the line does not supersede the current head of its claim key |
| `400 VALIDATION_ERROR` | Any other rule, such as `assertion.verification` or `designator.value` |
| `404 NOT_FOUND` | The anchored entity or subject does not exist |
| `403 FORBIDDEN` | Personal key, missing role, or suspended seat |
On `409`, fold the subject again to read the current head, then retry with `supersedes` set to it.
## Keep a copy in sync
Page `GET /records` from `afterSeq=0` and store each `line` verbatim. Resume from
`meta.nextAfterSeq`. Because the log is append-only and sealed, a synced copy stays valid, and the
offline checker can prove it matches what Y2 recorded. See
[Export STIX and verify the seal](/docs/ontology/how-to/export-and-verify).
## Read the fused view
Cyber-market fusion is an entity operation in the [Intel API](/docs/api/intel):
`GET /api/v2/entities/{entityId}/fusion` with `intel:explorer`. It uses the ledger's anchored
subjects and supply ties to build the exposure path.
## Endpoint reference
---
# Use the OpenAPI Contract (/docs/api/openapi)
Y2 publishes one OpenAPI 3.1 document as the machine-readable contract for its REST API. The
generated endpoint reference, API Workbench, repository TypeScript declarations, and contract
tests all consume that source.
```mermaid
flowchart LR
A[public/api/openapi.yaml] --> B[Fumadocs endpoint pages]
A --> C[API Workbench]
A --> D[TypeScript declarations]
A --> E[Response fixtures and contract tests]
B --> F[Generated reference navigation]
```
Use the generated [Endpoint Reference](/docs/api/reference) for operation details. Use this page
to locate, consume, or update the raw specification. Do not manually edit files under
`content/docs/api/reference` or `src/lib/openapi-reference-nav.generated.ts`.
## Get the specification
The production URL is:
```text
https://y2.dev/api/openapi.yaml
```
The app serves the same repository file at `/api/openapi.yaml` in development and production.
Agents and function-calling clients can use the equivalent JSON representation at:
```text
https://y2.dev/openapi.json
```
[Download the OpenAPI specification](/api/openapi.yaml)
## Compose the correct request URL
The document contains more than one server because v1 and v2 use different path shapes.
| Operation shape | Server | Example result |
| --- | --- | --- |
| v1 path such as `/reports` | `https://api.y2.dev/api/v1` | `https://api.y2.dev/api/v1/reports` |
| v2 literal path such as `/api/v2/incidents` | `https://api.y2.dev` operation override | `https://api.y2.dev/api/v2/incidents` |
OpenAPI tooling should honor an operation- or path-level `servers` value before the document-level
servers. Avoid blindly prepending `/api/v1` to an Intel v2 path.
## Read Y2 extensions
In addition to standard OpenAPI fields, operations can declare:
The standard `security` array remains authoritative for allowed authentication alternatives. Agent
Y2 operations are bearer-key only. Operations with an empty security alternative and `x-x402`
begin the payment flow without `Authorization`. The sanitized x402 receipt lookup is public.
## Import into an API client
### Import the production URL
Import `https://y2.dev/api/openapi.yaml` into a client that supports OpenAPI 3.1.
### Configure the server
Confirm that the selected operation uses the intended v1 or v2 server. Some clients expose the
document's server list as an environment selector.
### Add a scoped credential
Store `Y2_API_KEY` in the client's secret or environment facility and configure bearer auth as
`Bearer {{Y2_API_KEY}}`. Never commit an exported collection containing the key.
### Validate a read
Start with a bounded GET whose required scope is on the key. Check `X-Request-Id`, API version,
schema version, and rate-limit headers as well as the body.
## Generate types or a client
For a TypeScript project, generate path and component types directly:
```bash
npx openapi-typescript https://y2.dev/api/openapi.yaml \
--immutable \
--alphabetize \
--output src/y2-api.d.ts
```
For a full client, use an OpenAPI 3.1-compatible generator and review its handling of operation-level
servers, streaming responses, `application/problem+json`, NDJSON, GeoJSON, and x402 extensions.
Generated code is a starting point; SSE and payment flows commonly need explicit transport code.
## Update the contract in this repository
The editable source is `public/api/openapi.yaml`.
### Change the source specification
Update the operation, schema, response, examples, scopes, and x402 metadata together. Keep
operation IDs stable unless the operation itself is intentionally replaced.
### Regenerate derived artifacts
```bash
bun run docs:openapi
bun run generate:api-types
bun run generate:api-contract-fixtures
```
These commands update the Fumadocs operation pages and nav, immutable TypeScript declarations,
and schema-derived response fixtures.
### Run the contract gate
```bash
bun run check:api-contract
bun run docs:source
```
The contract gate lints the OpenAPI file, checks generated types, verifies the 54-operation
manifest against Convex HTTP registrations, validates response fixtures against schemas, and
runs focused tests for IDs, pagination, representations, projections, middleware, and webhook
identity.
### Review generated scope
Confirm that generated changes correspond to the intended source edit. Never patch a generated
endpoint page to conceal a mismatch in `public/api/openapi.yaml`.
`generate:api-contract-fixtures` derives representative bodies from the schemas. The contract
tests prove schema consistency and prevent persistence-field leakage; they do not prove that a
deployed environment currently returns those exact values.
## Repository artifacts
---
# Query the OSINT API (/docs/api/osint)
The OSINT API exposes source-oriented Situation Room data as canonical public resources. Use it for
normalized event feeds, geospatial observations, country surfaces, provider health, prediction
markets, transport and security feeds, or financial indicators.
The base URL is:
```text
https://api.y2.dev/api/v1/osint
```
## Choose an endpoint
### Events and geography
| Goal | Endpoint | Useful filters |
| --- | --- | --- |
| List normalized threat events | `GET /events` | `category`, `severity` |
| Load only geolocated map events | `GET /map` | `region` |
| Compose regional and spatial filters | `GET /regional` | `q`, `region`, `countryCode`, `sourceType`, `bbox`, radius, time |
| Read report-extracted events | `GET /y2-events` | `category`, `severity`, `countryCode` |
### Country intelligence
| Goal | Endpoint |
| --- | --- |
| Periodically generated intelligence brief | `GET /countries/{countryCode}/brief` |
| Primary stock index and weekly change | `GET /countries/{countryCode}/markets` |
| Country-linked prediction markets | `GET /countries/{countryCode}/predictions` |
| Recent news and report observations attributed to a country | `GET /countries/{countryCode}/news` |
| Country Conflict Indicators Index | `GET /countries/{countryCode}/cii` |
Country codes are ISO 3166-1 alpha-2 values such as `US` or `UA`.
### Specialized feeds
| Family | Endpoints |
| --- | --- |
| Conflict and security | `/cii`, `/cyber-threats`, `/military-posture`, `/gps-jamming` |
| Transport | `/aircraft`, `/vessels` |
| Markets | `/prediction-markets`, `/finint` |
| Operations | `/sources/status` |
Aircraft ingestion has been disabled since May 8, 2026, to reserve the shared Wingbits quota
for GPS interference detection. `/aircraft` and `/military-posture` can return empty or stale
data. `/gps-jamming` is unaffected.
## Authenticate or use x402
Every published OSINT operation accepts a bearer key with `osint:read` or its documented
[x402 pay-per-request flow](/docs/api/x402).
```bash
curl "https://api.y2.dev/api/v1/osint/events?limit=50" \
--header "Authorization: Bearer $Y2_API_KEY"
```
A request without `Authorization` begins with `402 Payment Required`. Retry it with the signed
payment header described in that response; do not send bearer and x402 credentials together.
## Search regional events
`/regional` combines text, source, category, severity, region, country, time, and coordinate
filters with AND semantics. Events without coordinates remain unless you request a bounding box,
radius, or `requireCoordinates=true`.
```bash
curl --get "https://api.y2.dev/api/v1/osint/regional" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "q=port disruption" \
--data-urlencode "sourceType=rss" \
--data-urlencode "category=economic" \
--data-urlencode "bbox=-10.0,35.0,40.0,65.0" \
--data-urlencode "datetime=2026-07-01T00:00:00Z/2026-07-21T23:59:59Z" \
--data-urlencode "limit=100"
```
`q` searches title and falls back to description substrings. `bbox` is exactly
`west,south,east,north` in WGS 84 and cannot cross the antimeridian. Radius search requires all
three of `nearLat`, `nearLon`, and `radiusKm`; the maximum radius is 20,000 km.
Use `datetime` for new integrations. It accepts an RFC 3339 instant or inclusive `start/end`
interval; `..` creates an open boundary. The inclusive epoch-millisecond `since` and `until`
parameters are deprecated and cannot be combined with `datetime`.
## Read country news
Use a two-letter country code to retrieve recent news and report observations:
```bash
curl "https://api.y2.dev/api/v1/osint/countries/SA/news?limit=8" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The country filter uses the observation's geographic attribution. A Saudi pipeline event can
qualify for `SA`; a regional Red Sea event does not qualify merely because Saudi Arabia is
mentioned or exposed to its effects. Available geographic evidence appears in
`provenance.geoResolution`, including the resolution method, matched text, and related or
mentioned country codes when available. These associations do not expand the country filter.
Country news includes unexpired `rss`, `gdelt`, `acled`, `news_terminal`, and `y2_report`
observations, excluding seismic and weather categories. Source and expiry filters apply before
the page limit, so satellite detections do not consume the news window. The default `limit` is
8 and the maximum is 20; follow `links.next` to continue.
The API returns canonical observations with stable `obs_` IDs. The country card can deduplicate
articles for display, so its headline count can differ from the API's observation count. The
[News API](/docs/api/news) serves a separate topic-feed cache and requires `news:read`; this
country-news endpoint requires `osint:read`.
If a country-news cursor is invalid or obsolete, the API returns HTTP 400 with
`VALIDATION_ERROR`. Restart without the cursor and follow the new continuation links. Integrations
carrying a cursor from before the [September country-news update](/docs/changelog/2026-09#more-dependable-country-news)
need to restart their collection after that rollout. A scan that exceeds its read budget returns
`QUERY_TOO_BROAD`; neither error is evidence that the country has no news.
See the [endpoint reference](/docs/api/reference/osint/getCountryNews) for the full response contract.
## Choose JSON, NDJSON, or GeoJSON
Representation support is operation-specific and listed on each generated reference page.
JSON collection responses contain `data`, `meta`, and `links`. Follow `links.next` until it is
`null`.
```bash
curl "https://api.y2.dev/api/v1/osint/cyber-threats?limit=100" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Supported collection routes return one canonical `data` row per line. Read continuation from
`X-Y2-Next-Cursor`.
```bash
curl "https://api.y2.dev/api/v1/osint/finint?format=ndjson&limit=100" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Spatial routes return an RFC 7946 FeatureCollection. Coordinates are longitude-first, and the
next page is the item in `links` whose `rel` is `next`.
```bash
curl "https://api.y2.dev/api/v1/osint/map?format=geojson&limit=200" \
--header "Authorization: Bearer $Y2_API_KEY"
```
You can use `Accept: application/x-ndjson` or `Accept: application/geo+json` instead of `format`.
Do not assume that every OSINT operation supports every representation: country briefs, country
stock indices, country CII, and source status return JSON only.
## Page collections safely
JSON collections expose an opaque, filter-bound cursor through `meta.page.nextCursor` and
`links.next`. Specialized representations move the cursor to `X-Y2-Next-Cursor` so their body can
stay representation-native. Preserve the original filters on the next request.
The maximum `limit` depends on the operation: for example, `/map` allows 500, `/regional` allows
200, and country prediction pages allow 10. Use the generated reference instead of assuming one
global maximum.
## Check source health before declaring absence
If a feed is empty or unexpectedly stale, inspect `/sources/status`:
```bash
curl "https://api.y2.dev/api/v1/osint/sources/status" \
--header "Authorization: Bearer $Y2_API_KEY"
```
| Field | Meaning |
| --- | --- |
| `state` | `closed` normally permits fetching; `open` isolates a failing source; `half_open` is a recovery probe state |
| `failureCount` | Consecutive failures; a successful fetch resets it to zero |
| `lastSuccessAt`, `lastSuccessAtISO` | Last success in epoch milliseconds and RFC 3339; nullable before any success |
| `lastFailureAt`, `lastFailureAtISO` | Most recent failure in both time formats |
| `lastError` | Most recent provider error when available |
Live-provider circuit breakers open after five consecutive failures and permit one probe after a
five-minute cooldown. A `y2_report` row represents report-enrichment health rather than a polled
provider: `open` means the newest recorded enrichment error is later than the latest success.
A healthy source does not prove completeness, and an empty endpoint does not prove that no
real-world event occurred. Preserve source attribution and corroborate high-impact conclusions.
## OSINT v1 or Intel v2?
| Use OSINT v1 when you need | Use [Intel v2](/docs/api/intel) when you need |
| --- | --- |
| Source-oriented normalized observations | Stable entity and incident identity |
| Country and provider-specific surfaces | Relationship and graph traversal |
| GeoJSON or NDJSON where advertised | Sparse semantic projections and bounded relations |
| Provider health and circuit-breaker state | Intel-specific FININT, cyber, or Explorer scopes |
---
# Manage Profiles through the API (/docs/api/profiles)
Profiles define recurring intelligence research: topic, schedule, status, instructions, and report
configuration. Subscriptions connect a profile to a delivery method. The API represents them as
separate resources.
```mermaid
flowchart LR
A[Profile prf_...] -->|schedule generates| B[Reports rpt_...]
C[Subscription sub_...] -->|follows| A
C -->|delivery preference| D[Email, SMS, or webhook]
```
`GET /api/v1/profiles` lists active subscriptions in the API key's user or workspace context.
Every row contains `subscription` and `profile`; it is not a flat catalog of every public
profile.
## Required scopes
| Operation | Scope |
| --- | --- |
| List subscribed profiles | `profiles:read` |
| Create, replace, patch, or delete an owned profile | `profiles:write` |
| Change subscription delivery or manage webhook configurations | `webhooks:manage` |
Profile creation and configuration features still follow the active workspace's
[plan limits](/docs/plans). API write access does not bypass profile, audio, branding, workspace,
or delivery entitlements.
## List subscriptions and profiles
```bash
curl "https://api.y2.dev/api/v1/profiles" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Each row has this shape:
```json
{
"subscription": {
"id": "sub_0123456789abcdef01234567",
"profileId": "prf_0123456789abcdef01234567",
"active": true,
"delivery": {
"method": "email",
"emailAudience": "individual",
"webhookId": null
}
},
"profile": {
"id": "prf_0123456789abcdef01234567",
"name": "Critical Supplier Risk",
"status": "active",
"frequency": "daily"
}
}
```
`profile` can be `null` if the subscribed resource no longer resolves. Save both IDs: `prf_...`
identifies research configuration, while `sub_...` identifies delivery preferences.
The list is not cursor-paginated today: `links.next` and `meta.page.nextCursor` are always `null`.
## Create an owned profile
Creation requires `name`, `topic`, `frequency`, and `scheduleTimeOfDay`. Times are UTC. The server
creates the profile as `active`, schedules it, and creates an active subscription for the same
tenant context.
```bash
curl "https://api.y2.dev/api/v1/profiles" \
--request POST \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: supplier-risk-2026-07-21" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting semiconductor suppliers, ports, export controls, severe weather, labor actions, and cyber incidents.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00",
"tags": ["supply-chain", "semiconductors"]
}'
```
Supported frequencies are `daily`, `weekly`, `biweekly`, and `monthly`. Weekly and biweekly
profiles can supply `scheduleDayOfWeek`; monthly profiles can supply `scheduleDayOfMonth`. See
[Scheduling](/docs/profiles/scheduling) for the product semantics, including the twice-monthly
biweekly schedule.
A successful create returns `201` with:
- the canonical `prf_...` resource in `data`
- `Location` containing its canonical API path
- `ETag` for that representation
- the supplied `Idempotency-Key`, when present
Idempotency keys must contain 8–200 letters, numbers, `.`, `_`, `:`, or `-`. They are tenant- and
operation-scoped for 24 hours. Reusing a key with a different canonical body returns
`409 IDEMPOTENCY_CONFLICT`.
## Patch one or more fields
Use `PATCH` when omitted fields must remain unchanged.
```bash
curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \
--request PATCH \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "If-Match: $PROFILE_ETAG" \
--data '{
"status": "paused",
"tags": ["supply-chain", "semiconductors", "paused-review"]
}'
```
Mutable fields include the profile's name, topic, status, schedule inputs, community visibility,
tags, custom instructions, report structure, search/model/budget/recursion/freshness/audio/tool
configuration, and branding template. The generated reference defines each nested input.
`PUT` and `PATCH /profiles/{profileId}` change profile configuration only. Use the subscription
delivery endpoint with the `sub_...` ID to select email, SMS, or a webhook.
## Replace mutable state
Use `PUT` only when sending the full intended mutable profile state. It requires the same four
fields as creation. Optional mutable fields omitted from a replacement reset to their defaults or
are cleared.
```bash
curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \
--request PUT \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor current disruption across strategic semiconductor supply chains.",
"frequency": "weekly",
"scheduleTimeOfDay": "08:00",
"scheduleDayOfWeek": "monday",
"tags": ["supply-chain"]
}'
```
Use the `ETag` returned by create or a previous update as `If-Match` when preventing a lost update.
`If-Match` is optional; a stale value returns `412 PRECONDITION_FAILED` and the current ETag.
There is currently no `GET /profiles/{profileId}` operation that returns an ETag.
## Change delivery on the subscription
Read `subscription.id` from `GET /profiles`, then call:
```bash
curl "https://api.y2.dev/api/v1/subscriptions/$SUBSCRIPTION_ID/delivery" \
--request PATCH \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"deliveryMethod": "webhook",
"webhookConfigId": "whk_0123456789abcdef01234567"
}'
```
Delivery methods are `email`, `sms`, `webhook`, and `both_email_sms`. A webhook delivery requires
an active, authorized `whk_...` configuration. For email-capable delivery, `emailAudience` can be
`individual` or, on a plan with member seats, `workspace`.
## Retrieve reports for a profile
The profile's `links.reports` value is the canonical filtered collection. You can also compose it
directly:
```bash
curl --get "https://api.y2.dev/api/v1/reports" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "profileId=$PROFILE_ID" \
--data-urlencode "limit=1"
```
This requires `reports:read` in addition to whichever profile scopes the client uses.
## Delete an owned profile
```bash
curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \
--request DELETE \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "If-Match: $PROFILE_ETAG"
```
Deletion is permanent. It cancels the scheduled job and deletes the profile's subscriptions,
signals, reports, and stored audio before returning `204` with no body. The API returns `404` when
the key cannot access or mutate the named profile, which avoids revealing another tenant's
resource.
In a shared workspace, write-role membership is required. Non-admin members can update or delete
only profiles they created; workspace administrators can manage tenant-scoped profiles.
---
# Projects API (/docs/api/projects)
Projects are owner-private intelligence workspaces. API requests derive the user and workspace from
the bearer key; callers cannot select another workspace in the request body.
## Scopes
| Operation | Scope |
| --- | --- |
| List or get Projects | `projects:read` |
| Create, update, archive, restore, or pin Projects | `projects:write` |
Both scopes require a workspace-bound API key. New API keys are available on Pro and Elite, even
though Projects are also available in the Lite application.
## Create a Project
```bash
curl -sS "https://api.y2.dev/api/v1/projects" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: project-supply-chain-2026-07" \
-d '{
"name": "Supply chain watch",
"objective": "Track disruptions affecting critical suppliers",
"instructions": "Prefer primary sources and call out conflicting evidence."
}'
```
The response returns a stable `prj_...` ID, `Location`, and `ETag`. Reusing an
`Idempotency-Key` with the same canonical JSON body returns the original Project.
## Read and update
```bash
curl -sS "https://api.y2.dev/api/v1/projects?status=active&limit=20" \
-H "Authorization: Bearer $Y2_API_KEY"
```
Project collections use the same opaque `nextCursor` and `links.next` pagination contract as other
Y2 collections. Send the returned cursor unchanged to retrieve older Projects.
Use `PATCH /api/v1/projects/{projectId}` for metadata, view defaults, pinning, and reversible
lifecycle changes. Send the latest `ETag` in `If-Match` when coordinating multiple writers.
```bash
curl -sS -X PATCH "https://api.y2.dev/api/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-H "If-Match: $PROJECT_ETAG" \
-d '{"status":"archived"}'
```
Archiving keeps Project data and pauses active Automations bound to that Project. Restore it with
`{"status":"active"}`.
Exact paths, parameters, request bodies, responses, and schemas
App views, private knowledge, evidence, resources, and lifecycle behavior
---
# OpenAI-compatible Agent Y2 chat stream (/docs/api/reference/agent-y2/createY2OpenAIChatCompletion)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Agent Y2 (/docs/api/reference/agent-y2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Stream a Y2 Agent response
OpenAI-compatible Agent Y2 chat stream
---
# Stream a Y2 Agent response (/docs/api/reference/agent-y2/streamAgentY2Chat)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Create an Automation (/docs/api/reference/automations/createAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get an Automation definition (/docs/api/reference/automations/getAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Automations (/docs/api/reference/automations)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List Automation definitions
Create an Automation
Get an Automation definition
Update or archive an Automation
List Automation run history
Request a manual Automation run
---
# List Automation run history (/docs/api/reference/automations/listAutomationRuns)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List Automation definitions (/docs/api/reference/automations/listAutomations)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update or archive an Automation (/docs/api/reference/automations/patchAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Request a manual Automation run (/docs/api/reference/automations/runAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Global Knowledge (/docs/api/reference/global-knowledge)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Retrieve Y2 Global Knowledge
---
# Retrieve Y2 Global Knowledge (/docs/api/reference/global-knowledge/retrieveGlobalKnowledgeV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Endpoint Reference (/docs/api/reference)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
API-key streams for the preconfigured Y2 Agent and compatibility endpoint
Report retrieval, text, audio, signals, and ontology graphs
Profile subscriptions, ownership, and configuration
Owner-private intelligence workspaces in the API key workspace
Durable Agent Y2 Automation definitions and run history
Webhook configuration for paid workspaces
Subscription delivery management
News Terminal items, feeds, and AI recaps
Situation Room events, feeds, country data, and source health
Ontology-backed v2 incidents, entities, markets, financial indicators, emergent signals, and cyber graphs.
Bounded, provider-neutral semantic and lexical retrieval across authorized Y2 reports and signal assessments.
x402 receipt lookup
The workspace passive entity ledger: an append-only, SHA-256-sealed log of subjects, designators, public observations, and claims with verification states and valid-time intervals.
---
# Cyber entity graph (/docs/api/reference/intel/getCyberGraphV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get cyber-market fusion for a company (/docs/api/reference/intel/getEntityFusionV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Traverse an entity relationship graph (/docs/api/reference/intel/getEntityGraphV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get an intel entity (/docs/api/reference/intel/getEntityV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get an intel incident (/docs/api/reference/intel/getIncidentV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Intel (/docs/api/reference/intel)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Read the public intelligence change feed
List intel incidents
Get an intel incident
List intel entities
Get an intel entity
List workspace company financial history
Get cyber-market fusion for a company
Traverse an entity relationship graph
List intel markets
List FININT indicators
List tagged emergent signals
Cyber entity graph
List CVE entities
List threat actor entities
---
# List CVE entities (/docs/api/reference/intel/listCVEsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Read the public intelligence change feed (/docs/api/reference/intel/listChangesV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List workspace company financial history (/docs/api/reference/intel/listCompanyFinancialObservationsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List intel entities (/docs/api/reference/intel/listEntitiesV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List FININT indicators (/docs/api/reference/intel/listFinintIntelV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List intel incidents (/docs/api/reference/intel/listIncidentsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List intel markets (/docs/api/reference/intel/listMarketsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List tagged emergent signals (/docs/api/reference/intel/listSignalsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List threat actor entities (/docs/api/reference/intel/listThreatActorsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# News (/docs/api/reference/news)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List news items
List AI-generated recaps
List available news feeds
---
# List available news feeds (/docs/api/reference/news/listFeeds)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List news items (/docs/api/reference/news/listNews)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List AI-generated recaps (/docs/api/reference/news/listRecaps)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Append a ledger record (/docs/api/reference/ontology-ledger/appendLedgerRecordV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Export a ledger subject as STIX 2.1 (/docs/api/reference/ontology-ledger/getLedgerSubjectStixV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Fold a ledger subject (/docs/api/reference/ontology-ledger/getLedgerSubjectV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Ontology Ledger (/docs/api/reference/ontology-ledger)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List ledger subjects
Open a ledger subject
Fold a ledger subject
Export a ledger subject as STIX 2.1
Export sealed ledger lines
Append a ledger record
Verify the ledger seal
---
# Export sealed ledger lines (/docs/api/reference/ontology-ledger/listLedgerRecordsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List ledger subjects (/docs/api/reference/ontology-ledger/listLedgerSubjectsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Open a ledger subject (/docs/api/reference/ontology-ledger/openLedgerSubjectV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Verify the ledger seal (/docs/api/reference/ontology-ledger/verifyLedgerV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# AI-generated intelligence brief (/docs/api/reference/osint/getCountryBrief)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Country instability index (/docs/api/reference/osint/getCountryCII)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Country-specific news (/docs/api/reference/osint/getCountryNews)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Prediction markets (/docs/api/reference/osint/getCountryPredictions)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Stock market index (/docs/api/reference/osint/getCountryStockIndex)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# OSINT (/docs/api/reference/osint)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List threat events
List map events with coordinates
Conflict Indicators Index
AI-generated intelligence brief
Stock market index
Prediction markets
Country-specific news
Country instability index
Military posture by theater
Tracked military aircraft
Naval vessel positions
GPS interference zones
Data source health
List cyber threat indicators
List prediction markets
Y2 report-extracted events
Search regional OSINT events
Financial intelligence indicators
---
# Tracked military aircraft (/docs/api/reference/osint/listAircraft)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Conflict Indicators Index (/docs/api/reference/osint/listCII)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List cyber threat indicators (/docs/api/reference/osint/listCyberThreats)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Financial intelligence indicators (/docs/api/reference/osint/listFinintIndicators)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# GPS interference zones (/docs/api/reference/osint/listGPSJamming)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Military posture by theater (/docs/api/reference/osint/listMilitaryPosture)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List threat events (/docs/api/reference/osint/listOsintEvents)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List map events with coordinates (/docs/api/reference/osint/listOsintMapEvents)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Search regional OSINT events (/docs/api/reference/osint/listOsintRegionalEvents)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List prediction markets (/docs/api/reference/osint/listPredictionMarkets)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Data source health (/docs/api/reference/osint/listSourceStatus)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Naval vessel positions (/docs/api/reference/osint/listVessels)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Y2 report-extracted events (/docs/api/reference/osint/listY2Events)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# x402 receipt lookup (/docs/api/reference/payments/getX402Receipt)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Payments (/docs/api/reference/payments)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
x402 receipt lookup
---
# Create an intelligence profile (/docs/api/reference/profiles/createProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Delete a profile (/docs/api/reference/profiles/deleteProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get a subscribed profile (/docs/api/reference/profiles/getProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Profiles (/docs/api/reference/profiles)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List subscribed profiles
Create an intelligence profile
Get a subscribed profile
Update a profile (partial)
Delete a profile
Update a profile (full replacement)
---
# List subscribed profiles (/docs/api/reference/profiles/listProfiles)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update a profile (partial) (/docs/api/reference/profiles/patchProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update a profile (full replacement) (/docs/api/reference/profiles/updateProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Create a Project (/docs/api/reference/projects/createProject)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get a Project (/docs/api/reference/projects/getProject)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Projects (/docs/api/reference/projects)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List owned Projects
Create a Project
Get a Project
Update, archive, or restore a Project
---
# List owned Projects (/docs/api/reference/projects/listProjects)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update, archive, or restore a Project (/docs/api/reference/projects/patchProject)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get a report (/docs/api/reference/reports/getReport)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report audio (/docs/api/reference/reports/getReportAudio)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get TTS-preprocessed report text (/docs/api/reference/reports/getReportAudioText)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report ontology graph (/docs/api/reference/reports/getReportGraph)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report emergent signals (/docs/api/reference/reports/getReportSignals)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report as plain text (/docs/api/reference/reports/getReportText)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Reports (/docs/api/reference/reports)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List intelligence reports
Get a report
Get report emergent signals
Get report ontology graph
Get report audio
Get report as plain text
Get TTS-preprocessed report text
---
# List intelligence reports (/docs/api/reference/reports/listReports)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Subscriptions (/docs/api/reference/subscriptions)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Update subscription delivery method
---
# Update subscription delivery method (/docs/api/reference/subscriptions/updateDelivery)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Create a webhook configuration (/docs/api/reference/webhooks/createWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Delete a webhook configuration (/docs/api/reference/webhooks/deleteWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Webhooks (/docs/api/reference/webhooks)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List webhook configurations
Create a webhook configuration
Delete a webhook configuration
Update a webhook configuration
Test a webhook endpoint
---
# List webhook configurations (/docs/api/reference/webhooks/listWebhooks)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Test a webhook endpoint (/docs/api/reference/webhooks/testWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update a webhook configuration (/docs/api/reference/webhooks/updateWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Retrieve Reports through the API (/docs/api/reports)
Use the Reports API to retrieve published intelligence generated for profiles. Start with compact
report metadata, then request only the representation or extracted artifact your integration needs.
## Choose a representation
| Consumer | Representation |
| --- | --- |
| Agent | Markdown with cited sources |
| Application | Compact or expanded JSON |
| Analytics workflow | Signals or graph |
| Human reader or listener | Plain text or audio |
## Required scopes
| Operation | Bearer scope |
| --- | --- |
| List, detail, Markdown, plain text, signals, graph, and TTS-preprocessed text | `reports:read` |
| Audio metadata or CDN redirect | `reports:audio` |
The audio operation also depends on audio entitlement and an available recording. Every Report
operation supports [x402 pay-per-request](/docs/api/x402) as an alternative to a bearer key.
## List reports
For a bearer-key request without `profileId`, Y2 lists reports newest first across the active
profiles subscribed in that key's user or workspace context.
```bash
curl --get "https://api.y2.dev/api/v1/reports" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "limit=20"
```
To target one profile, pass its canonical `prf_...` ID:
```bash
curl --get "https://api.y2.dev/api/v1/reports" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "profileId=$PROFILE_ID" \
--data-urlencode "limit=20"
```
The endpoint supports only `profileId`, `limit`, `cursor`, and `format`; it does not provide a date
range filter. `limit` defaults to 20 and is capped at 100.
An anonymous x402 caller has no subscription context, so `profileId` is required for
`GET /reports`. Single-report and subresource calls identify the resource with `reportId`.
JSON pages return `data`, `meta`, and `links`. Follow `links.next` until it is `null`. For NDJSON,
send `format=ndjson` or `Accept: application/x-ndjson`; each line is one compact report and the next
cursor is in `X-Y2-Next-Cursor`.
## Read compact report metadata
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The default response contains the canonical `rpt_...` and `prf_...` IDs, topic, summary, published
timestamps, language, signal and graph counts, audio availability, profile identity, and links to
each representation. It does not include the full report body or full source rows by default.
An authenticated detail or subresource request must be subscribed to the report's profile. A
missing report returns `404`; an authenticated key without that subscription returns `403`.
## Expand structured JSON
Use the comma-separated `include` parameter to embed bounded related data.
| Include | Added field |
| --- | --- |
| `content` | `content.markdown` and its media type |
| `sources` | Canonical source records with `src_...` IDs and available attribution |
| `signals` | Report-local emergent decision signals |
| `graph` | Report-local ontology nodes, edges, incidents, and citations when available |
| `audio` | Audio representation metadata or `null` |
```bash
curl --get "https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "include=content,sources,signals"
```
`view=agent` is shorthand for `content,sources,signals`. It does not add the ontology graph or
audio representation.
## Choose Markdown or plain text
Request the canonical stored report representation:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/markdown"
```
`format=markdown` is equivalent. This returns the report body directly, not a JSON envelope.
Request content with HTML stripped:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/text" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/plain"
```
Omit `Accept: text/plain` to receive JSON with `text`, `wordCount`, and an estimated
`readingTimeMinutes` at approximately 200 words per minute.
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/text" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Use Markdown for grounded agents and publishing pipelines. Use `/text` when the consumer cannot
render markup or needs reading-time metadata.
## Retrieve signals or the report graph
The subresources avoid transferring the full report body:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/signals" \
--header "Authorization: Bearer $Y2_API_KEY"
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/graph" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Signals include decision fields, confidence, time horizon, normalized subjects, entity links when
resolved, tags, and cited sources. Reports created before signal extraction return an empty
`signals` array.
The graph is a bounded snapshot extracted from that report, not a live traversal of the full Intel
ontology. It contains report-local nodes, relationships, incident anchors, citations, and counts.
Reports created before graph extraction return `graph: null` and zero counts.
## Retrieve narration or TTS text
Request audio metadata with `reports:audio`:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/audio" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The JSON body contains the MP3 URL, duration, formatted duration, media type, and file size when
known. To redirect directly to the CDN asset, pass `redirect=true` or send an `Accept` header that
contains an audio media type. The redirect is `302`, and the redirect response can be cached for
24 hours. A report without audio returns `404 NO_AUDIO`.
`/audio-text` requires `reports:read`, not `reports:audio`. It returns the exact preprocessing path
used for narration, including pronunciation normalization and applicable profile branding:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/audio-text" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The duration fields on this endpoint are estimates based on character count; they are not measured
audio duration.
Reports, summaries, signals, and ontology graphs are synthesized intelligence. Preserve source
URLs and confidence fields, and verify high-impact conclusions against primary evidence.
---
# Configure Report Webhooks (/docs/api/webhooks)
Use a webhook when your application should react as soon as Y2 publishes a report. This guide
covers configuration and consumption through the public API. For the delivery product overview,
see [Webhook delivery](/docs/delivery/webhooks).
```mermaid
flowchart LR
A[Create whk_ config] --> B[Test endpoint]
B --> C[Attach to sub_ subscription]
C --> D[Receive CloudEvent]
D --> E[Verify signature]
E --> F[Deduplicate by event ID]
F --> G[Fetch rpt_ representations]
```
## Before you begin
You need:
- a paid workspace plan with webhook delivery;
- an API key with `webhooks:manage`;
- a public HTTPS endpoint that accepts `POST` requests; and
- a secret generated and stored by your application if you want signed deliveries.
Y2 rejects loopback, private, link-local, cloud metadata, multicast, and other reserved network
destinations. In production, webhook URLs must use HTTPS. Custom headers must have string values;
`Host`, `Content-Length`, and `Content-Type` cannot be configured.
## 1. Create a webhook configuration
Send a stable `Idempotency-Key` when a client may retry the request:
```bash
curl --request POST "https://api.y2.dev/api/v1/webhooks" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: create-primary-webhook-01" \
--data '{
"name": "Primary report receiver",
"url": "https://example.com/webhooks/y2",
"secret": "replace-with-a-random-secret",
"headers": {
"X-Integration": "intelligence-pipeline"
}
}'
```
A successful request returns `201`, a canonical `whk_...` ID, `Location`, and `ETag`. Store the ID;
you need it to test the endpoint and attach it to a subscription.
List and mutation responses reveal whether signing is configured and return custom header names,
but never return the secret or custom header values.
## 2. Test the receiver
```bash
curl --request POST "https://api.y2.dev/api/v1/webhooks/$WEBHOOK_ID/test" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The test request is a CloudEvents 1.0 event with type `dev.y2.webhook.test.v1`. Y2 waits up to 10
seconds. A `2xx` response from your receiver produces `200`; a timeout, network error, or non-`2xx`
response produces `422`.
The test verifies connectivity and signing, but it does not attach the configuration to a report
subscription.
## 3. Attach the webhook to a subscription
Use the subscription's canonical `sub_...` ID, not its profile ID:
```bash
curl --request PATCH \
"https://api.y2.dev/api/v1/subscriptions/$SUBSCRIPTION_ID/delivery" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--data "{\"deliveryMethod\":\"webhook\",\"webhookConfigId\":\"$WEBHOOK_ID\"}"
```
The webhook must be active and belong to the same user or workspace scope as the subscription.
Changing a profile does not change its subscription delivery settings.
## 4. Process the report event
Production deliveries use structured CloudEvents with media type
`application/cloudevents+json` and event type `dev.y2.report.generated.v1`:
```json
{
"specversion": "1.0",
"id": "y2mexamplelogicalevent01",
"source": "https://api.y2.dev/api/v1/profiles/prf_0123456789abcdef01234567",
"type": "dev.y2.report.generated.v1",
"subject": "reports/rpt_0123456789abcdef01234567",
"time": "2026-07-21T18:00:00.000Z",
"datacontenttype": "application/json",
"dataschema": "https://api.y2.dev/schemas/events/report-generated-v1.json",
"data": {
"report": {
"id": "rpt_0123456789abcdef01234567",
"profileId": "prf_0123456789abcdef01234567",
"summary": "A concise report summary.",
"status": "published",
"generatedAt": "2026-07-21T17:59:42.000Z",
"language": "en",
"intelligence": { "signalCount": 3, "graphNodeCount": 12 },
"audio": { "status": "available", "durationSeconds": 284 }
},
"subscription": { "id": "sub_0123456789abcdef01234567" },
"links": {
"report": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567",
"markdown": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?format=markdown",
"sources": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?include=sources",
"signals": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/signals",
"graph": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/graph",
"audio": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/audio"
}
}
}
```
The event is intentionally compact. Use the supplied links and a key with the relevant Reports
scope to retrieve full Markdown, sources, signals, graph, or audio.
## Delivery headers
| Header | Meaning |
| --- | --- |
| `Content-Type` | `application/cloudevents+json` |
| `User-Agent` | `Y2-Webhook-Delivery/3.0` |
| `X-Y2-Timestamp` | Attempt time as Unix seconds |
| `X-Y2-Event-Id` | Stable logical event ID; matches the body `id` |
| `X-Y2-Attempt-Id` | Unique transport-attempt ID |
| `X-Y2-Attempt` | One-based attempt number |
| `Idempotency-Key` | Stable logical event ID |
| `X-Y2-Signature` | `sha256=` when a secret is configured |
Configured custom headers are sent first. Y2's delivery headers take precedence if names collide.
## Verify the signature
Compute HMAC-SHA256 over the exact raw request bytes. Do not parse and reserialize the JSON before
verification.
```typescript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyY2Webhook(
rawBody: Buffer,
signatureHeader: string | null,
secret: string,
): boolean {
if (!signatureHeader) return false;
const expected = Buffer.from(
`sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`,
);
const actual = Buffer.from(signatureHeader);
return actual.length === expected.length && timingSafeEqual(actual, expected);
}
```
Also compare `X-Y2-Timestamp` with your current time and reject values outside your replay window;
five minutes is a practical default. Only return `2xx` after the event has been durably accepted.
## Handle attempts and failures
The event body and `X-Y2-Event-Id` remain stable if the same logical delivery is attempted again.
`X-Y2-Attempt-Id` and `X-Y2-Attempt` identify the transport attempt. Deduplicate on the body `id`,
`X-Y2-Event-Id`, or `Idempotency-Key` before starting downstream work.
Each event has at most five automatic attempts, with a 10-second timeout and delays of 5 seconds,
30 seconds, 5 minutes, and 30 minutes. Network errors, timeouts, HTTP 408/429, and 5xx responses are
retried. Other HTTP rejections pause the destination immediately. Five consecutive failures across
a destination also pause it; successful delivery resets that counter. Retries reuse the original
payload and event ID and do not restart report generation.
The configuration owner receives one warning email on the first temporary failure and one email
when delivery is paused. Paused destinations receive no further automatic attempts. Reports,
configuration, and delivery history remain available; other destinations continue independently.
`GET /webhooks` exposes status and delivery health, including the failure reason/status, last
success, suspension time, and last successful test. To recover:
1. Save repaired configuration with `PUT`, keeping `isActive: false`.
2. Call `POST /webhooks/{webhookId}/test`; disabled destinations can be tested. A successful
response includes the configuration's new `ETag`, because testing updates its delivery health.
3. Within 15 minutes, `PUT` the same configuration with `isActive: true`, using the test
response's `ETag` in `If-Match`. Include the same secret and headers: changing the endpoint,
signing secret, or headers invalidates the test.
4. In **Delivery history and replay** in webhook settings, explicitly replay retained failed
events. Enabling delivery does not automatically replay the backlog.
Enabling without a current successful test returns `409 WEBHOOK_TEST_REQUIRED`. A test failure
does not consume the production retry budget.
## Replace or delete a configuration
`PUT /webhooks/{webhookId}` replaces the complete mutable configuration. `name` and `url` are
required. Omitted `secret` and `headers` are cleared; omitted `isActive` preserves the current
status. Send the latest returned `ETag` in
`If-Match` when you have one to prevent overwriting a concurrent change.
Before deleting a webhook, move every attached subscription to another delivery method or webhook.
Deletion returns `409 WEBHOOK_IN_USE` while any subscription still references the configuration;
a successful deletion returns `204` with no body.
---
# Explore the API with Workbench (/docs/api/workbench)
The API Workbench turns Y2's live OpenAPI description into a browsable endpoint catalog, parameter
editor, request runner, and snippet generator.
Open **[Developers → API Workbench](/app/developers/api-workbench)** in the dashboard.
New API access is available on Pro and Elite. Existing grandfathered Lite API users retain
Workbench access during the grace period.
## What Workbench reads from OpenAPI
The page loads `/api/openapi.yaml` and uses the current contract to show:
- endpoint categories, methods, paths, summaries, and operation IDs;
- required bearer scopes from `x-required-scopes`;
- path and query parameter descriptions, types, enums, examples, and defaults; and
- operation- or path-level server overrides, including the Intel v2 base URL.
Parameters use enum selectors, boolean toggles, and numeric or text inputs where applicable.
Required path and query values are checked before a live request leaves the browser.
The current Workbench editor does not render OpenAPI request-body fields. Use it directly for
`GET`, bodyless `POST`, and other path/query-driven operations. For profile creation, webhook
configuration, delivery updates, and other JSON-body mutations, start from the generated
endpoint reference or the relevant API guide.
## Run a read request
### Paste an API key
Enter a key in the password field at the top of the page. The value remains in React component
state and is added as `Authorization: Bearer ...` only when you run a request.
### Open a category and endpoint
Categories begin collapsed. Open one such as **Intel**, **OSINT**, **Reports**, or **News**, then
choose an endpoint. The row shows its method, path, scope, parameter counts, and operation ID.
### Fill required values
Path parameters are always required. Required query parameters are shown separately; enable
only the optional query parameters you want to send.
### Run the request
Select **Run request**. Workbench calls the canonical production API from your browser and shows
the response status and parsed body inline. JSON is formatted; non-JSON bodies are shown as text.
If a required path or query value is missing, Workbench produces a local `400` explanation instead
of sending an incomplete request. A missing key produces a local `401` prompt.
Requests use the workspace associated with the pasted key. Switching your active workspace in
the app does not change the key's workspace.
## Generate a copy-safe snippet
Each expanded endpoint provides **curl**, **TypeScript**, and **Python** tabs. The generated code:
- references `$Y2_API_KEY`, `process.env.Y2_API_KEY`, or `os.environ["Y2_API_KEY"]`;
- never includes the key pasted into Workbench;
- substitutes an unset required path or query value with an `UPPER_SNAKE_CASE` environment
variable; and
- adds comments for the required and currently enabled query parameters, including available
descriptions, types, enums, and defaults.
For example, after enabling `limit` without entering a value, a curl snippet can contain:
```bash
# Required:
# countryCode (path) — ISO 3166-1 alpha-2 country code
#
# Optional:
# limit (query · integer · default: 8) — Maximum results
curl "https://api.y2.dev/api/v1/osint/countries/$COUNTRY_CODE/news?limit=$LIMIT" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Accept: application/json"
```
Set the placeholders before running the copied command:
```bash
export Y2_API_KEY="y2_..."
export COUNTRY_CODE="UA"
export LIMIT="8"
```
Best for one-off checks and reproducing a request in a bug report. Unset values use shell
variables such as `$COUNTRY_CODE`.
Uses native `fetch` and reads secrets or missing values from `process.env`. Move the API key to
server-side configuration before integrating the snippet into an application.
Uses `httpx`, reads configuration from `os.environ`, calls `raise_for_status()`, and applies a
30-second client timeout.
## Use the AI Quickstart prompt
**Copy prompt** copies a short instruction that directs a coding agent to the canonical Agentic DX
and MCP guides before it integrates Y2. It contains documentation URLs and safety guidance, not the
API key entered on the page.
Use this prompt as orientation, then give the agent the exact endpoint, scope, and outcome you need.
Keep `Y2_API_KEY` in the agent runtime or MCP client environment—not in the prompt or tool arguments.
## Know the current boundaries
| Capability | Current behavior |
| --- | --- |
| API target | Production server from OpenAPI; falls back to `https://api.y2.dev/api/v1` |
| Path and query fields | Interactive and OpenAPI-driven |
| Request body fields | Not rendered; use endpoint reference examples |
| Live response | Status and response body |
| Response headers | Not displayed in the inline pane |
| Authentication | Bearer key required by the runner |
| x402 | Use curl or an x402 client outside Workbench |
| Saved state | Key, expanded rows, and parameter values are page component state, not durable presets |
Workbench is an integration aid, not a replacement for application-level error handling. Before
shipping copied code, add timeouts, typed response handling, pagination, retry policy, and secret
management appropriate to your runtime.
---
# Pay per Request with x402 (/docs/api/x402)
x402 lets an agent or application pay for a supported Y2 read request without creating an account
or API key. Y2 advertises an x402 v2 payment requirement, verifies and settles the signed payment,
then serves the normal endpoint response.
```mermaid
sequenceDiagram
participant C as Client
participant Y as Y2 API
participant F as Facilitator
C->>Y: Request without Authorization
Y-->>C: 402 + PAYMENT-REQUIRED
C->>C: Validate and sign requirement
C->>Y: Same request + PAYMENT-SIGNATURE
Y->>F: Verify payment
F-->>Y: Valid
Y->>F: Settle payment
F-->>Y: Transaction
Y->>Y: Run resource handler
Y-->>C: Resource response + PAYMENT-RESPONSE
```
## Choose bearer auth or x402
| Mode | Use it for |
| --- | --- |
| Bearer API key | Recurring integrations, workspace-scoped data, writes, and plan rate limits |
| x402 | Anonymous, one-off, agent-driven access to supported public read operations |
A request with `Authorization: Bearer ...` always takes the API-key path. An invalid bearer token
returns its normal `401` or `403`; Y2 does not silently fall back to x402. Omit `Authorization`
entirely when you intend to pay per request.
## Supported API surfaces
x402 is enabled on registered read operations in these families:
- Reports, including Markdown, text, signals, graph, audio, and TTS text;
- News items, recaps, and feed discovery;
- OSINT feeds and country intelligence;
- Intel v2 incidents, entities, markets, financial intelligence, signals, cyber, and changes.
It is not an alternative for API-key-only surfaces such as profile management, webhook
configuration, or Agent Y2. Check an operation's `security` and `x-x402` fields in the
[generated endpoint reference](/docs/api/reference) before building a payment flow.
Anonymous callers do not have a user or workspace context. For example, `GET /reports` requires
a canonical `profileId` query parameter on the x402 path.
## Read the payment challenge
Send the exact resource request without a bearer key:
```bash
curl --include \
"https://api.y2.dev/api/v1/news?topics=markets&limit=5"
```
The `402` response includes a base64-encoded `PAYMENT-REQUIRED` header and the decoded requirement
as JSON. Its shape is similar to:
```json
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "https://api.y2.dev/api/v1/news?topics=markets&limit=5",
"description": "Real-time direct-source news terminal items",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "2000",
"payTo": "0x...",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}
],
"extensions": {
"bazaar": { "discoverable": true }
}
}
```
Do not hard-code `asset`, `payTo`, EIP-712 domain fields, or the amount from this example. Read and
validate the returned requirement every time. `amount` is expressed in atomic token units; USDC
uses six decimals, so `"2000"` represents `$0.002`.
Production requirements use Base mainnet (`eip155:8453`). Development deployments can use Base
Sepolia (`eip155:84532`). The network is a CAIP-2 identifier, not the legacy `base` slug.
## Make a paid request in TypeScript
The following flow matches the x402 packages used by the current Y2 implementation:
```bash
npm install @x402/core@^2.18.0 @x402/evm@^2.18.0 viem@^2.55.2
```
Create the signer in a server-side runtime or secure wallet environment. Never expose a raw private
key in browser code.
```typescript
import { x402Client } from "@x402/core/client";
import { x402HTTPClient } from "@x402/core/http";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const privateKey = process.env.X402_PRIVATE_KEY as `0x${string}` | undefined;
if (!privateKey) throw new Error("X402_PRIVATE_KEY is required");
const signer = privateKeyToAccount(privateKey);
const core = new x402Client().register("eip155:*", new ExactEvmScheme(signer));
const x402 = new x402HTTPClient(core);
const url = "https://api.y2.dev/api/v1/news?topics=markets&limit=5";
const challengeResponse = await fetch(url, { headers: { Accept: "application/json" } });
if (challengeResponse.status !== 402) {
throw new Error(`Expected an x402 challenge, received ${challengeResponse.status}`);
}
const challengeBody = await challengeResponse.json();
const requirement = x402.getPaymentRequiredResponse(
(name) => challengeResponse.headers.get(name),
challengeBody,
);
const option = requirement.accepts[0];
if (!option || option.scheme !== "exact" || option.network !== "eip155:8453") {
throw new Error("Y2 returned an unsupported payment option");
}
const maxAtomicUsdc = 2_000n;
if (BigInt(option.amount) > maxAtomicUsdc) {
throw new Error("Payment exceeds the client price ceiling");
}
const payment = await x402.createPaymentPayload(requirement);
const paidResponse = await fetch(url, {
headers: {
Accept: "application/json",
...x402.encodePaymentSignatureHeader(payment),
},
});
const settlement = paidResponse.headers.has("PAYMENT-RESPONSE")
? x402.getPaymentSettleResponse((name) => paidResponse.headers.get(name))
: null;
const result = await paidResponse.json();
console.log({ status: paidResponse.status, settlement, result });
```
The price ceiling above is specific to the `$0.002` News example. Set a ceiling appropriate to the
operation you selected. Keep the first and second request URL, method, query, and body identical.
## Understand the headers
| Header | Direction | Meaning |
| --- | --- | --- |
| `PAYMENT-REQUIRED` | Response | Base64-encoded x402 v2 challenge |
| `PAYMENT-SIGNATURE` | Request | Base64-encoded signed payment payload |
| `PAYMENT-RESPONSE` | Response | Base64-encoded settlement result |
| `Authorization` | Request | Selects bearer authentication when present |
`X-PAYMENT` and `X-PAYMENT-RESPONSE` are legacy x402 v1 names. New Y2 clients should use the v2
headers above.
## Prices and wallet limits
Each operation maps to one current tier. The generated endpoint page is the source of truth for the
operation's exact price.
| Tier | Price | Per wallet/minute | Per wallet/day |
| --- | ---: | ---: | ---: |
| Health/meta | $0.001 | 60 | 10,000 |
| Standard read | $0.002 | 60 | 5,000 |
| Enriched data | $0.01 | 30 | 2,000 |
| Premium report list | $0.05 | 10 | 500 |
| Premium single report | $0.25 | 10 | 200 |
| Premium report audio | $0.50 | 5 | 100 |
Y2 also applies a global per-endpoint circuit breaker. A wallet limit returns `429`; an endpoint
capacity limit returns `503`. Both are checked before settlement, so the rejected request is not
charged.
## Store and inspect a receipt
The EIP-3009 authorization inside the signed payment contains a nonce. Treat that nonce as a receipt
lookup capability and store it with the request result:
```bash
curl "https://api.y2.dev/api/v1/x402/receipts/$PAYMENT_NONCE"
```
The lookup is public to anyone who knows the nonce. It returns the payer address, endpoint, USD
amount, network, status, and—when available—the transaction hash and settlement time. It does not
return the signature, token authorization, pay-to address, or full payment payload.
| Receipt status | Meaning |
| --- | --- |
| `verify_failed` | The facilitator rejected the payment |
| `rate_limited` | Wallet or endpoint limits blocked the request before settlement |
| `replay_blocked` | The payer and nonce combination was already recorded |
| `settle_failed` | Verification passed but settlement did not complete |
| `settled` | Payment settled and Y2 invoked the resource handler |
| `handler_failed` | Payment settled, then the resource handler threw |
A settled payment can still be followed by an application response such as `400`, `404`, or a
temporary data-readiness error. Validate resource IDs and parameters before signing, inspect the
paid response status and body, and retain the nonce and `PAYMENT-RESPONSE` metadata for support.
## Handle failures
| Status | Typical cause |
| --- | --- |
| `401` or `403` | Invalid bearer key or missing bearer scope; no x402 fallback |
| `402` | Missing or invalid payment, unsupported payment payload, or nonce replay |
| `429` | Per-wallet minute or daily limit exceeded |
| `503` | x402 unavailable, settlement infrastructure unavailable, or endpoint circuit breaker open |
| Other endpoint status | Payment settled, then the normal resource handler returned its own result |
Never reuse an authorization nonce. When a result is ambiguous, check the receipt before creating a
new payment so you do not confuse a settled request with a transport failure.
---
# Desktop App (/docs/apps)
Y2 Desktop puts your chats, projects, reports, and intelligence workspace in a dedicated app for
Mac and Windows. The installer bundles the application and its runtime. You do not need Node.js,
Bun, developer tools, or a separate browser installation to run it.
The [Y2 download page](/download) offers **unofficial, unsigned beta** installers alongside
release notes and checksums. They install as **Y2** and use your existing account and workspace.
Apple notarization and Windows publisher signing are planned for a future release.
If the page says **Downloads not published yet**, use [Y2 in your browser](/login).
## Requirements
| Platform | Requirements | Installer |
| --- | --- | --- |
| Mac | macOS 12 or later; Apple silicon or Intel | Universal `.dmg` |
| Windows | Windows 10 or later; 64-bit x64 processor | `.exe` setup wizard |
An internet connection and a Y2 account are required. Desktop uses the same cloud service,
workspace data, plan, and usage limits as the web app. It does not provide an offline copy of
your workspace or run AI models on your device. No separate desktop subscription is required.
## Install on Mac
1. Open [Y2 downloads](/download) and select **Download for Mac** when available.
2. Open the downloaded `.dmg` file and drag **Y2** into **Applications**.
3. Eject the disk image, then open Y2 from Applications or Launchpad.
4. This beta is unsigned and not notarized. After macOS blocks the first launch, open
**System Settings → Privacy & Security → Open Anyway** for Y2 if you choose to trust this
download. Confirm **Open** when prompted. See [Apple's instructions](https://support.apple.com/102445).
5. Sign in with the email address or phone number you already use for Y2, then enter your
verification code. New users can create their workspace through the same sign-in flow.
## Install on Windows
1. Open [Y2 downloads](/download) and select **Download for Windows** when available.
2. Open the downloaded `.exe` installer. An **Unknown publisher** warning is expected for this
unsigned beta. If SmartScreen offers **More info → Run anyway**, you can use it after verifying
the download's checksum. Smart App Control or organizational policies may block unsigned apps
without an override; use [Y2 in your browser](/login) on those devices.
3. Follow the setup wizard to choose the installation location and complete installation.
4. Open **Y2** from the Start menu or desktop shortcut.
5. Sign in with your existing Y2 account, or complete the new-workspace setup.
## Verify a download
The download page links the release notes and a versioned `SHA256SUMS.txt` file. To check file
integrity, calculate the installer's SHA-256 digest and compare the full result with its entry
in the checksum file. Replace the example path below with your downloaded file's path.
On macOS, use Terminal:
```sh
shasum -a 256 "/path/to/Y2-installer.dmg"
```
On Windows, use PowerShell:
```powershell
Get-FileHash "C:\path\to\Y2-installer.exe" -Algorithm SHA256
```
If the digest differs, discard the file and download it again from [Y2 downloads](/download).
Checksums verify file integrity. These unofficial builds do not have a verified publisher signature.
## Use your workspace
Your saved chats, projects, intelligence profiles, and reports are shared with the web app.
Changes synchronize through Y2's cloud service while you are online. Select the same workspace
on each device to see the same information. Feature availability follows your
[workspace plan](/docs/plans).
Billing opens securely in your default browser. After checkout or managing a subscription,
choose **Open Y2 Desktop** on the return page. If your browser does not open the app, reopen Y2
directly and check **Settings → Billing**. Billing status may take a moment to refresh.
For research and account workflows, the rest of the [Y2 documentation](/docs) applies to desktop
as well.
## Sign-in on your device
Y2 connects directly to its cloud API using your normal sign-in. You do not need a developer
account or a deployment key. The desktop app saves sign-in credentials with your operating
system's encryption so you can reopen it without signing in each time.
On Mac, this can prompt for access to Y2's Keychain item. If secure storage is locked or access
is denied, unlock it and choose **Try again** in Y2. **Clear saved sign-in** removes the local
credentials so you can sign in again. Signing out also removes the saved credentials; your
cloud workspace remains available.
## Updates
These unofficial builds use **manual updates**. Download the latest installer from
[Y2 downloads](/download), quit Y2, and install the new version. Your account and cloud workspace
remain the same. The app may display its internal channel as **local**; automatic updates are
not configured for this build. Signed automatic updates are planned for a future release.
If Y2 requires a newer version to connect to the service, follow the update prompt or download
the current release manually. You can continue in the web app while resolving an installation
or update problem.
## Installation troubleshooting
| Problem | What to do |
| --- | --- |
| No download button | The public release is still being prepared. Use the web app and check the download page for availability. |
| macOS cannot verify the developer | Follow the Open Anyway steps above for this unsigned beta. If macOS reports damage or malware, compare the checksum and contact support; do not disable Gatekeeper globally. |
| Windows reports an unknown publisher | This is expected for the unsigned beta. Verify the checksum and use the per-app SmartScreen exception if available. If device policy or Smart App Control blocks it, use the browser app. |
| The app opens but cannot connect | Check your internet connection and whether Y2 opens in your browser. A work network may need to allow Y2's service and download endpoints. |
| A page fails to load | Use the recovery screen to retry or restart Y2. If it continues, use the web app and report the app version and operating-system version. |
| An update fails | Check your connection and available disk space, then retry. You can install the latest release from the official download page without creating a new account. |
| Workspace data looks different | Check the signed-in account and selected workspace on both devices. Desktop has the same workspace permissions as the web app. |
For help, email [support@y2.dev](mailto:support@y2.dev). Include your operating-system version,
Y2 version and channel, and the exact error message. Do not send passwords or verification codes.
## Uninstall
Quit Y2 before uninstalling. On macOS, move the application from Applications to Trash. On
Windows, uninstall Y2 from **Settings → Apps**. Removing the desktop application does not delete
your cloud workspace or cancel your subscription. Manage your account and billing in Y2.
Check the current Mac and Windows release and its installation files.
Continue with your existing account and cloud workspace.
---
# Audio Reports (/docs/audio)
Audio Reports turn a generated intelligence report into an MP3 narration. The feature is
available in active Pro and Elite workspaces and is currently labeled **Alpha** in the profile
editor.
Audio is configured per profile. Enabling narration affects future report runs; it does not
generate audio for reports that already exist.
## Enable narration for a profile
### Open the profile editor
Go to **InfoOps → My Profiles**, then create a profile or edit an existing one.
### Open Advanced
Continue to the **Advanced Configuration** step and find **Audio Narration**.
### Turn on audio
Enable the switch. Free and Lite workspaces cannot enable it; the backend also rejects an
enabled audio configuration when the active workspace lacks the entitlement.
### Choose the narration settings
Preview and select a voice, choose a generation speed, and optionally assign a synced
pronunciation dictionary. If no voice is selected, Y2 uses its default professional
narrator.
### Save the profile
Audio is generated the next time that profile produces a non-degraded report.
The voice library groups available voices into News & Broadcast, Professional, Narrative &
Documentary, and Casual & Conversational. The stored catalog can include English and Spanish
voices with masculine, feminine, or neutral labels. Availability is data-driven, so the exact
voice list can change.
The current dictionary selector lists all dictionaries in the active scope; it does not filter
them to the selected voice's language. Check the language badge yourself before assigning one.
## What happens during generation
Y2 prepares a speech-specific version of the report before calling the text-to-speech provider:
- It removes the trailing references section, citation markers, HTML, and Markdown formatting.
- It normalizes headings, punctuation, common entities, numbers, currency, acronyms, and tickers
for spoken output.
- It adds the assigned branding template's audio intro, before-report audio block, after-report
audio block, and outro. When no value is configured, Y2 branding defaults apply.
- It applies the assigned pronunciation dictionary only when that dictionary has a provider ID
and a `synced` status.
- It generates an MP3 with Cartesia Sonic-3 at 44.1 kHz and 128 kbps, then stores the file and its
metadata in Convex.
```mermaid
flowchart LR
A[Generate report] --> B[Prepare speech text]
B --> C[Apply branding and synced dictionary]
C --> D[Cartesia Sonic-3]
D --> E[MP3 in Convex storage]
E --> F[In-app player]
E --> G[Email link]
E --> H[API and webhook status]
```
Audio output is limited to 20 MB. Generation is also skipped when its estimated cost would push
the report above a configured per-report budget.
Audio is an optional workflow step. A missing API key, provider error, oversize result, budget
limit, or degraded report can leave the report without audio. Report delivery continues when
the audio step returns no file.
The stored duration is an estimate based on the prepared text, not a measurement read from the
finished MP3. The in-app player replaces it with the browser-reported media duration after the
file loads.
## Create a pronunciation dictionary
Pronunciation dictionaries are also an Alpha feature. Pro workspaces can store up to 10 and Elite
workspaces up to 100. Each dictionary can contain up to 200 entries.
### Open Audio
Go to **InfoOps → Audio** and select **Create** under Pronunciation Dictionaries.
### Define the dictionary
Enter a name, optional description, and English or Spanish language. Add entries individually
or use the CSV bulk-import control.
### Add replacements
Each entry stores a source word, a pronunciation value, and an Alias or IPA label. The current
provider sync sends entries as text-to-alias replacements, regardless of the stored label, so
use a readable replacement string and test the generated result.
### Save and verify sync
Creating or editing a dictionary starts an automatic provider sync. Confirm that its badge
becomes **Synced**. If it becomes **Error**, inspect the displayed error and use the refresh
button to retry after correcting the entries.
### Assign it to a profile
Return to the profile's **Advanced Configuration**, enable Audio Narration, select the
dictionary, and save.
| Constraint | Enforced limit |
| --- | ---: |
| Dictionaries in Pro | 10 |
| Dictionaries in Elite | 100 |
| Entries per dictionary | 200 |
| Dictionary name | 50 characters |
| Description | 200 characters |
| Source word | 100 characters |
| Replacement | 300 characters |
Editing entries moves the dictionary back to `pending` before it is synced again. If a selected
dictionary is pending, errored, deleted, or outside the profile's tenant scope, Y2 does not apply
it during generation.
## Customize spoken branding
The **InfoOps → Audio** page also exposes the intro and outro for each branding template in the
active workspace. Use `{name}` to insert the profile name.
| Spoken content | Limit | Position |
| --- | ---: | --- |
| Audio intro | 100 characters | Before all other narration |
| Before-report audio | 200 characters | After the intro and optional label |
| After-report audio | 200 characters | Before the outro and optional label |
| Audio outro | 200 characters | At the end |
The before- and after-report audio fields live in the full
[Branding Templates editor](/docs/profiles/branding). A template must be explicitly assigned to
the profile for its custom values to apply.
## Play and deliver audio
When audio is available, the report page displays **Listen**. The bottom-sheet player supports
play and pause, seeking, download, and playback rates of 0.75x, 1x, 1.25x, 1.5x, and 2x. Voice
and duration metadata are displayed when available.
Delivery surfaces expose audio differently:
| Surface | Current behavior |
| --- | --- |
| In-app report | Shows the player only when the report has an audio URL |
| Email | Shows an “Audio Narration Available” callout linking to the report page; the MP3 is not attached |
| Webhook | Reports `available` or `unavailable`, an estimated duration, and an API audio link |
| Reports API | Returns audio metadata or redirects to the stored MP3 URL |
Audio files are removed when their owning report, profile, workspace, or account is deleted and
can also be affected by storage cleanup. Do not treat a returned storage URL as a permanent
archive.
## Retrieve audio through the API
Use an API key with the `reports:audio` scope, or complete the endpoint's x402 flow. The path uses
the report's public API ID.
```bash
curl --request GET \
--url "https://api.y2.dev/api/v1/reports/REPORT_ID/audio" \
--header "Authorization: Bearer YOUR_API_KEY"
```
```json
{
"data": {
"url": "https://example.convex.cloud/api/storage/…",
"duration": 847,
"durationSeconds": 847,
"durationFormatted": "00:14:07",
"format": "mp3",
"mimeType": "audio/mpeg",
"fileSizeBytes": 13420800
}
}
```
`duration` is retained for compatibility and is deprecated; use `durationSeconds`. Add
`?redirect=true`, or request an audio media type with the `Accept` header, for a `302` response to
the stored file URL.
The related `GET /api/v1/reports/{reportId}/audio-text` endpoint requires `reports:read` and
returns the speech-preprocessed text, character count, and estimated duration without requiring
that an MP3 already exists. See the generated [Reports reference](/docs/api/reference/reports) for
canonical schemas, errors, rate limits, and x402 pricing.
## Troubleshoot audio
Confirm that the active workspace—not another personal or team workspace—is Pro or Elite.
Free and Lite workspaces do not have the audio entitlement.
The button appears only after audio metadata is stored. Confirm that narration was enabled
before the run. Provider configuration, a degraded report, the 20 MB limit, or a per-report
budget can cause audio to be skipped without blocking report delivery.
Confirm that the profile has the intended dictionary selected and that the dictionary badge
says Synced. Check the dictionary and voice language manually because the current selector
does not enforce a match.
Generation stores an estimate based on text length. The player reads the MP3's actual duration
from browser media metadata after loading, so the value can change.
Verify the public report ID, report access, and that audio exists for the report. The endpoint
uses the same not-found response when no audio is available.
## Next steps
Configure the report workflow that produces narration.
Add intros, outros, and channel-specific static content.
Retrieve reports and their derived representations programmatically.
Compare audio access and pronunciation-dictionary quotas.
---
# Create an Automation (/docs/automations/creating)
This tutorial creates an Automation, activates it, and verifies its first run.
## Before you begin
You need a Pro or Elite active workspace and a write-capable workspace role. If the Automation
will use a Project or profile trigger, create that resource in the same workspace first.
## 1. Open Automations
Open **Automations** from the app navigation. If you arrived from a Project, the form can start
with that Project selected.
## 2. Define the work
Select **New Automation**, enter a name, and write the instruction Agent Y2 should execute. Keep
the instruction focused on one repeatable outcome. The instruction can contain up to 8,000
characters.
## 3. Choose the context
Optionally select a Project. Y2 accepts only an active Project owned by you in the active
workspace. Leaving Project blank creates a workspace-scoped output thread.
## 4. Choose a trigger
For a schedule, choose daily, weekly, or monthly, then set the local time and timezone. Weekly
schedules also require a weekday; monthly schedules require a day from 1 through 28.
For a report trigger, select an active InfoOps profile from the same owner and workspace. The
Automation runs after a completed report event is accepted.
## 5. Choose notifications and status
Choose notifications for all runs, failures only, or none. Save as **Draft** while reviewing the
definition, or save as **Active** to accept triggers. Active schedule definitions receive their
next scheduled run.
## 6. Verify the Automation
Use **Run now** on an active definition. Review run history for queued, running, succeeded,
failed, skipped, or canceled status, then open the output thread when it is available.
Y2 skips overlapping runs, stale definition revisions, lost plan or membership access, and
inaccessible Projects. A skipped run does not bypass workspace or owner checks.
---
# Automations (/docs/automations)
Automations run a bounded Agent Y2 instruction without requiring someone to open Chat. An
Automation can run on a schedule or after a selected InfoOps profile finishes a report, and it can
optionally keep its output inside a Project.
Automations are not included with Free or Lite. Pro workspaces can keep up to 10 active, paused,
or draft definitions; Elite workspaces can keep up to 100. The limit is shared by the workspace.
## Definition and run separation
The Automation definition stores its name, instruction, trigger, notification policy, optional
Project, and current revision. Every execution creates a separate run snapshot. Editing a
definition does not rewrite prior run history.
Only one run for a definition can execute at a time. When a trigger arrives while another run is
active, the newer run is skipped instead of overlapping.
## Trigger types
| Trigger | Behavior |
|---|---|
| Schedule | Daily, weekly, or monthly in the selected IANA timezone. |
| Profile report completed | Runs once for each accepted completion event from the selected active profile. |
| Manual | **Run now** creates an immediate run from an active definition. |
Profile triggers must reference a profile owned by the same user in the same workspace. A
Project-bound Automation must also reference an active Project with the same owner and workspace.
```mermaid
flowchart LR
trigger[Schedule or report event] --> admission[Workspace and entitlement check]
admission --> run[Agent Y2 run]
run --> thread[Workspace or Project output thread]
run --> history[Run history and notification]
```
## Access changes and downgrade safety
Y2 checks current workspace membership, plan access, definition revision, and Project access again
when a run starts. If Automations are no longer available, Y2 skips the run, pauses the definition,
and cancels its schedule. A downgraded owner can still pause or archive an existing definition so
cleanup is not blocked by the upgrade gate.
## Cost and output
Each accepted run reserves a bounded Agent Y2 budget, then settles actual model and tool costs or
refunds the reservation after a failed or skipped run. Output is written to the definition's
workspace-bound thread. Project-bound output stays in that Project.
## Elite governance
Elite audit logs bind Automation creation, edits, status changes, and manual run requests to the
acting user and workspace. Run records also retain the owner, workspace, trigger, definition
revision, billing state, and output identifiers.
## Choose the next task
Configure a trigger, instruction, Project, and notification policy.
Review limits, statuses, triggers, run states, and access checks.
Build the private objective and context an Automation can operate within.
---
# Automations Reference (/docs/automations/reference)
## Availability and limits
| Plan | Access | Shared workspace definition limit |
|---|---|---:|
| Free | No | 0 |
| Lite | No | 0 |
| Pro | Yes | 10 |
| Elite | Yes | 100 |
Draft, active, and paused definitions count toward the limit. Archived definitions do not.
Definitions are owner-private even in an Elite organization.
## Definition fields
| Field | Constraint or value |
|---|---|
| Name | Required, up to 100 characters. |
| Instructions | Required, up to 8,000 characters. |
| Operation | `agent_y2` |
| Overlap policy | `skip_if_running` |
| Status | Draft, active, paused, or archived. |
| Notification policy | All runs, failures only, or none. |
| Project | Optional active Project with the same owner and workspace. |
## Schedule triggers
| Frequency | Required fields |
|---|---|
| Daily | Time of day and IANA timezone. |
| Weekly | Time of day, timezone, and weekday. |
| Monthly | Time of day, timezone, and day of month. |
## Run states
`queued` → `running` → `succeeded` or `failed` is the normal path. A run can instead become
`skipped` when admission checks fail or another run overlaps, or `canceled` when its work is
explicitly stopped.
Billing states are `reserved`, `settled`, `released`, and `none`. A released state means the
request hold was returned without creating settled usage. Each run snapshots the
definition revision, instruction, name, trigger, workspace, optional Project, and cost metadata.
## Live admission checks
Before paid work starts, Y2 verifies:
- The definition is active and still at the queued revision.
- The owner is still a member of the definition workspace.
- The workspace still has Pro or Elite Automation access and Chat capacity.
- A bound Project is active, owner-matched, workspace-matched, and entitled.
- No earlier run for the definition is currently active.
These checks apply to schedules, report events, manual runs, and workflow continuation.
---
# March 2026 Release Archive (/docs/changelog/2026-03)
[← Back to the latest release](/docs/changelog)
## March 2026: Y2SOURCE Event Provenance Engine
This entry describes the March 2026 launch. At launch, Y2SOURCE was available on **Lite** and
**Pro**. See [Plans and limits](/docs/plans) and the [OSINT API guide](/docs/api/osint) for
current access and request behavior.
Y2 intelligence reports now feed the Situation Room. The **Y2SOURCE extraction pipeline** extracts
structured geopolitical events from reports generated by the Y2 Orchestration Engine. Each event
includes provenance metadata linking it to the source report.
### What changed
#### Event extraction from reports
- Every Y2 intelligence report is now processed through a structured extraction pipeline that
identifies discrete geopolitical events, including conflicts, political shifts, economic
signals, and cyber incidents.
- Extracted events include geographic coordinates, severity, and temporal analysis such as
`ongoing`, `past`, `forecast`, or `analysis`.
- Named-entity recognition captures people, organizations, countries, and locations referenced in
each event.
#### Situation Room integration
- Y2-sourced events appear on the interactive globe alongside external OSINT feeds.
- Y2 markers distinguish report-derived intelligence from direct provider observations.
- Standard map filters such as category, severity, and region apply to Y2 events.
#### Provenance metadata
- Each Y2 event carries provenance metadata that connects the observation to report-derived
extraction context.
- Confidence scores from 0 to 1 reflect the extraction model's certainty about each event.
- Temporal classification indicates whether an event is ongoing, historical, forecast, or
analytical.
### Query report-extracted events
`GET /api/v1/osint/y2-events` returns only observations with `sourceType: "y2_report"`. It accepts
`category`, `severity`, `countryCode`, `limit`, and `cursor`; Bearer-token requests require the
`osint:read` scope.
```bash
curl -H "Authorization: Bearer y2_your_key" \
"https://api.y2.dev/api/v1/osint/y2-events?category=conflict&severity=high&limit=10"
```
The current JSON response uses stable public IDs and cursor pagination:
```json
{
"data": [
{
"id": "obs_7Jf2kQ9mV4sP",
"type": "observation",
"sourceType": "y2_report",
"title": "Escalation in Eastern Mediterranean Naval Activity",
"description": "Multiple carrier groups repositioned...",
"category": "conflict",
"severity": "high",
"coordinates": { "lat": 34.5, "lon": 33.0 },
"geometry": { "type": "Point", "coordinates": [33.0, 34.5] },
"locationName": "Eastern Mediterranean",
"countryCode": "CY",
"region": "middle_east",
"eventTime": 1784023200000,
"eventTimeISO": "2026-07-14T10:00:00.000Z",
"occurredAt": "2026-07-14T10:00:00.000Z",
"observedAt": null,
"url": "https://example.com/source",
"provenance": {
"source": "y2_report",
"confidence": 0.92
}
}
],
"meta": {
"pageCount": 1,
"limit": 10,
"hasMore": false,
"nextCursor": null
},
"links": {
"self": "/api/v1/osint/y2-events?category=conflict&severity=high&limit=10",
"next": null
}
}
```
See the generated [`GET /osint/y2-events` reference](/docs/api/reference/osint/listY2Events) for
all parameters, representations, and error responses.
### How the pipeline works
```mermaid
flowchart LR
report["Generate report"] --> extract["Extract events"]
extract --> normalize["Normalize places and entities"]
normalize --> observe["Upsert observations"]
observe --> resolve["Link provenance and incidents"]
```
The pipeline identifies structured events, resolves locations and entities, and upserts each event
as an observation with `sourceType: "y2_report"`. Internal lineage retains the report, model, and
confidence context used during extraction.
Y2-sourced observations have a 14-day time-to-live. Expired rows are excluded from API and app
queries and removed by OSINT maintenance.
---
## Profile sharing defaults
Community profile sharing is now **enabled by default** when creating new profiles. You can still
turn sharing off in profile settings.
---
## Situation Room map enhancements
- Y2 report-sourced events now render with distinct Y2-branded markers on the globe.
- Map-layer rendering for Y2 provenance events was improved.
---
# June 2026 Release Archive (/docs/changelog/2026-06)
> Historical release notes: plan prices and quotas below describe the release at that time.
> For current Elite allowances, see [Plans and Limits](/docs/plans).
[← Back to the latest release](/docs/changelog)
This archive preserves what changed in June 2026. Follow its links to feature and API guides for
current access, limits, and request contracts.
## June 30, 2026: News Terminal, Lite trial signup, and platform reliability
Y2 now includes the rebuilt News Terminal, a simpler Lite trial signup path, and reliability
improvements across billing, chat, delivery, developer tools, and intelligence data quality.
### What changed
#### News Terminal
- The News Terminal now uses direct-source feeds and appears throughout the product as the Y2
News Terminal / Signal Feed, replacing legacy Gloria-branded copy in active customer surfaces.
- Users can select source feeds, switch between Live Feed and 12h Recap views, refresh the active
view, and keep feed/view preferences across reloads.
- Recaps now summarize the same signals shown in the live feed, keeping summaries aligned with
the terminal while reducing unnecessary source refreshes.
- News API and reference docs now describe the Y2 News Terminal consistently, with clearer
responses for invalid topics, invalid timeframes, and feeds that are still initializing.
#### Lite trial signup and billing
- New signup now guides users toward a Lite trial by default while keeping Free available as a
fallback path.
- Lite checkout starts a 7-day trial and returns users to briefing setup once subscription access
is confirmed.
- If checkout finishes before subscription access has synced, users now see a pending setup state
with actions to check again or return to plan selection instead of being redirected prematurely.
- Trialing subscriptions now unlock paid access consistently across briefing setup, chat, usage
tracking, and billing views.
- Billing views now show clearer workspace subscription status, owner-only billing controls, and
profile reclaim notices after plan changes.
#### App, Chat, and delivery reliability
- App shell navigation and scrolling are more stable on mobile and desktop.
- Chat, profile setup, report delivery, audio, SMS, and webhook workflows received reliability
improvements around usage tracking, delivery state, and customer-facing errors.
- Report and briefing layouts now handle long reports, delivery preferences, branding, and profile
setup more clearly.
#### Developer and intelligence workflows
- API Workbench endpoints are now grouped into collapsible categories, making larger API surfaces
easier to scan.
- Generated API examples keep pasted API keys out of copied snippets, and the workbench includes
a copyable AI Quickstart prompt for teams integrating Y2.
- Aircraft tracks and normalized entity identifiers now feed the intelligence graph more
consistently, reducing duplicate entities and improving relationship context across OSINT and
Explorer views.
---
## June 18, 2026: Chat reliability, OSINT Monitor, and landing performance
Y2 now hardens chat image generation, streamlines the OSINT monitor workspace, and adds
landing-page performance guardrails for responsive marketing assets.
### What changed
#### Chat and configuration reliability
- Image generation now reserves chat credits before external model calls, refunds reserved credits
on provider, upload, or storage failures, and avoids double charging when generated images are
saved.
- Chat stream error handling now returns a stable `INTERNAL_ERROR` code and logs redacted identity
metadata instead of exposing raw token or subject values.
- Convex client setup and chat HTTP URL derivation now fail fast when `VITE_CONVEX_URL` is missing.
- Open Graph report routes derive public URLs from `SITE_URL`, with `https://y2.dev` as the
production fallback.
- Chat web-search source chips now ignore malformed or unsupported URLs instead of throwing during
render.
#### OSINT Monitor experience
- The monitor bottom bar now uses four resizable panes: live video, theater headlines, FININT, and
intel findings.
- CNBC is promoted to the first live-news channel, and the removed duplicate news terminal gives
more room to the analysis panels.
- Lite workspaces now unlock the full Situation Room experience: live tracking, naval vessels,
GPS interference, markets, prediction signals, anomaly/convergence/cascade analysis, exports,
and the full paid OSINT limits.
- The monitor layer sidebar now treats every Situation Room layer as Lite-accessible, so Lite users
can enable the same map layers available to Pro and Elite workspaces.
#### Landing-page performance
- Walkthrough images now use versioned responsive `srcSet` metadata, intrinsic dimensions, eager
first-image loading, and lower-priority lazy loading for later images.
- New 960px marketing image derivatives reduce walkthrough screenshot payloads, with immutable
Netlify caching for `/images/marketing/*`.
- `bun run check:landing-perf` validates stale hero preloads were removed, marketing images stay
versioned and responsive, and responsive derivatives remain smaller than their originals.
---
## June 2026: Y2 Elite B2B, Organization Workspaces and Intel API v2
Elite pricing changed to **$200/month** or **$2,000/year** on August 31, 2026, for an organization
workspace with up to **5 members**. See [Plans and Limits](/docs/plans) for current pricing and
entitlements. The June launch prices below are historical.
Y2 Elite launched in June as the flat-priced B2B organization plan at **$500/mo flat** or
**$5,000/yr**, including one organization workspace with up to **5 members**, shared governance,
audit logs, the highest API/profile/workspace limits, and expanded Intel API access.
Y2 Workspaces now support the current organization model for companies, teams, and departments.
Personal Free, Lite, and Pro workspaces continue to serve individual workflows; Elite creates a
shared organization workspace where profiles, reports, API keys, usage, delivery settings, and
governance resolve from the active workspace.
```mermaid
flowchart LR
people["Owner and members"] --> workspace["Active organization workspace"]
workspace --> intel["Profiles and reports"]
workspace --> delivery["Delivery and branding"]
workspace --> api["API keys and usage"]
workspace --> governance["Roles and audit logs"]
```
This release also expands the Intel API v2 surface served from `https://api.y2.dev/api/v2`. The new
Intel surface is gated by `intel:finint`, `intel:cyber`, and `intel:explorer` scopes. As of July 21,
2026, new API-key access to those scopes requires Pro or Elite. Existing Lite
API users retain their keys at historical Lite limits; Lite retains in-app
FININT/Cyber/Explorer, and the in-app Cyber dashboard remains available on Free.
### What changed
#### Organization workspaces
- Elite creates a shared B2B workspace for organizations, teams, and departments.
- Owners and admins can invite members, manage roles, and review workspace governance.
- Workspace resources are loaded and protected server-side, including profiles, subscriptions,
reports, audio, branding templates, delivery settings, API keys, and usage.
- API keys belong to the active workspace and enforce workspace-scoped resource access, scopes,
IP allowlists, and aggregate rate limits.
#### Elite pricing and limits
| Feature | Elite |
|---------|-------|
| June launch price (superseded) | $500/mo flat or $5,000/yr; [current pricing](/docs/plans) |
| Members | Up to 5 per organization workspace |
| Profiles | 50 total custom and community profiles |
| Branding Templates | 25 |
| API Keys | 25 |
| API Rate Limits | 120 req/min, 50,000 req/day |
| Chat Budget | 5,000 credits/month |
| Situation Room | 50 layers, 100 alert rules, 50 watchlist countries |
| OSINT Refresh & Retention | 1 min refresh, 90 day snapshot retention |
| Pronunciation Dictionaries | 25 |
Elite includes 10x Pro daily API capacity, 5x Pro API-key capacity, 5x Pro chat budget, 2.5x Pro
profile capacity, and organization-only governance.
#### B2B governance and safety
- Workspace members and audit logs are available on Elite organization workspaces.
- Team usage summaries and API-key activity are available from workspace governance views.
- Invite acceptance activates the paid organization workspace while preserving each user's
separate personal Free workspace.
- Free-trial report limits remain user-global, so creating additional workspaces does not mint new
trial capacity.
#### Intel API v2
The new Intel API exposes Y2's ontology layer through HTTP endpoints: deduplicated incidents,
canonical entities, entity-relationship graphs, prediction and Y2 markets, emergent signals, and
financial intelligence. It serves the same data as the in-app Explorer, FinInt, and Cyber sections.
The 11 endpoints shipped in this launch accept both API keys and
[x402 pay-per-request](/docs/api/x402):
The additive `GET /api/v2/changes` feed was released later. The current surface therefore has 12
paths; use the [Intel API guide](/docs/api/intel) and generated reference for the live inventory.
| Method | Endpoint | Tier | Scope |
|--------|----------|------|-------|
| `GET` | `/api/v2/incidents` | T1 ($0.001) | `intel:explorer` (or `finint`/`cyber` by category) |
| `GET` | `/api/v2/incidents/{id}` | T2 ($0.002) | scope resolved from incident category |
| `GET` | `/api/v2/entities` | T1 ($0.001) | `intel:explorer` |
| `GET` | `/api/v2/entities/{id}` | T2 ($0.002) | `intel:explorer` |
| `GET` | `/api/v2/entities/{id}/graph` | T3 ($0.01) | `intel:explorer` |
| `GET` | `/api/v2/markets` | T1 ($0.001) | `intel:finint` |
| `GET` | `/api/v2/finint` | T1 ($0.001) | `intel:finint` |
| `GET` | `/api/v2/signals` | T3 ($0.01) | `intel:finint`, `intel:cyber`, or `intel:explorer` by domain |
| `GET` | `/api/v2/cyber/graph` | T3 ($0.01) | `intel:cyber` |
| `GET` | `/api/v2/cyber/cves` | T1 ($0.001) | `intel:cyber` |
| `GET` | `/api/v2/cyber/actors` | T1 ($0.001) | `intel:cyber` |
New Intel scopes:
- `intel:finint` — markets, finint indicators, non-cyber incidents, market/supply-chain signals
- `intel:cyber` — CVEs, threat actors, malware families, cyber graph, cyber incidents,
cyber/technology signals
- `intel:explorer` — universal entity catalog, entity drill, entity graph, any-category incidents,
broad signal queries
Every v2 response stamps `X-Y2-API-Version: 2.0` so programmatic clients can branch on surface. The
in-app [API Workbench](/app/developers/api-workbench) lists Intel endpoints in a dedicated Intel
group, the full reference lives in [Intel API documentation](/docs/api/intel), and the OpenAPI spec
at `/api/openapi.yaml` includes all 11 v2 paths with operation-level `servers:` overrides.
#### Everything in Pro, plus team scale
Elite includes Pro capabilities such as full Situation Room access, audio narration, SMS and
webhook delivery, scheduled reports, advanced research, FININT/Cyber/Explorer API scopes,
investigation sharing, live/vessel/market tracking, anomaly/convergence/cascade analysis, and data
export.
```bash
curl -H "Authorization: Bearer $Y2_API_KEY" \
"https://api.y2.dev/api/v2/incidents?category=cyber&severity=high&limit=10"
```
---
# July 2026 Release Archive (/docs/changelog/2026-07)
> Historical release notes: plan prices and quotas below describe the release at that time.
> For current Elite allowances, see [Plans and Limits](/docs/plans).
[← Back to the latest release](/docs/changelog)
This archive preserves what changed in July 2026. Follow its links to the maintained
feature and API guides for current behavior.
## July 30, 2026: Privacy, product platform, and installed-app foundation
Y2 strengthens its privacy terms, simplifies product analytics and feedback, and lays the
foundation for installed desktop and Android applications.
### Clearer data-processing terms
- The [Data Processing Agreement](/dpa) now defines customer and Y2 processing roles, United States
service-provider restrictions, security and breach duties, deletion and audit support, and
international transfer safeguards.
- New processing, subprocessor, and security schedules explain the data involved, each provider's
purpose, and Y2's technical and organizational safeguards.
- Customers now receive at least 30 days' notice before a new subprocessor materially processes
Customer Personal Data.
### One consent-controlled product platform
- Y2 removed Statsig and UserJot and consolidated product analytics, masked session replay, surveys,
and in-app Support in PostHog.
- Optional browser analytics remain consent-controlled. Session replay masks visible text and form
inputs, and reviewed product events exclude user-authored report, profile, and investigation
content.
- The Privacy Policy, DPA subprocessor schedule, and internal data-privacy audit now describe the
same current processing.
### Platform and installed-app foundation
- The web application and backend now use a shared Bun and Turborepo workspace, with common
translation and desktop contracts.
- Electron and Android shells add secure renderer boundaries, scoped device permissions, update and
packaging checks, and shared application behavior. Native signing and store distribution remain
separate release gates.
---
## July 24, 2026: Projects, Automations, and workspace governance
Y2 added private Project workspaces and durable Agent Y2 Automations with plan-aware access,
workspace isolation, and Elite governance.
### Projects on Lite, Pro, and Elite
- **Projects** organize chats, reports, profiles, graph boards, generated images, evidence, and
private documents around one objective.
- Graph, Timeline, Situation, Evidence, Knowledge, and Overview views keep related intelligence
connected without publishing Project content to Global Knowledge.
- Projects are bound to the active workspace and their owner. Switching workspaces cannot expose
another workspace's Project chats, resources, evidence, or private knowledge.
- Project sharing was not included in this release; Elite members did not automatically gain access
to another member's private Projects.
Follow the [Projects overview](/docs/projects) or
[create your first Project](/docs/projects/getting-started).
### Automations on Pro and Elite
- **Automations** run bounded Agent Y2 instructions daily, weekly, monthly, or after a selected
InfoOps profile completes a report.
- Pro includes up to **10** Automation definitions and Elite includes up to **50**, shared across
the active workspace. Free and Lite do not include Automations.
- An Automation can write to a workspace-bound output thread or stay inside an active Project.
- Live admission checks stop stale, overlapping, cross-workspace, downgraded, or inaccessible
Project runs before paid work continues.
- Run history records trigger, definition revision, output, status, and billing settlement; owners
can safely pause or archive definitions after a downgrade.
See [Automations](/docs/automations) and the
[Automation reference](/docs/automations/reference).
### Workspace governance and language support
- Elite audit records bind material Project and Automation changes to the acting user and
workspace without changing owner-private content access.
- Project and Automation navigation, plan guidance, upgrade states, forms, run history, and landing
explanations are available in English and Spanish.
- Plan, workspace, report-workflow, and profile documentation reflects the same enforced
entitlement boundaries.
---
## July 21, 2026: Global coverage, Chat updates, and Pro API keys
Y2 expanded global intelligence coverage, simplified active research, and tightened API-key
access.
### Intelligence
- New Situation Room visitors start with all **27 map layers** enabled. Existing preferences stay
unchanged, and mobile markers are easier to select.
- Discover adds monthly digests for **72 cities** and **39 industries**. Chat and onboarding now
recommend a matching public digest before creating a duplicate custom brief.
- News Terminal expands to **40 global topics** across geopolitics, markets, industries, regions,
AI, and crypto. High-signal items join Y2's ontology with deduplication, entity links, and source
provenance.
- Incident enrichment and corroboration now run together, with automatic recovery for interrupted
work and protection from stale retries.
Explore [News Terminal](/docs/news), [OSINT sources](/docs/osint/sources), or the
[News API](/docs/api/news).
### Chat
- New chats default to **GLM 5.2** using Zero Data Retention routing. Existing threads and saved
model choices stay unchanged.
- Featured models are now Nemotron 3 Ultra, Grok 4.5, GLM 5.2, DeepSeek V4 Pro, Kimi K3, Qwen 3.6,
GPT-5.6 Luna, Claude Sonnet 5, Gemini 3.6 Flash, and Mistral Small.
- Tool activity now uses a compact, auto-scrolling panel with clearer progress and failure states.
See [Chat Features](/docs/chat) for model selection and privacy details.
### API access
- New API keys require **Pro** (5 keys, 30 requests/minute, 5,000/day) or **Elite** (25 keys, 120
requests/minute, 50,000/day).
- Existing Lite keys keep their historical limits (10 requests/minute, 500/day) and can still be
rotated or revoked. Lite cannot create more keys, and future downgrades are not grandfathered.
Webhooks remain available on Lite, and x402 access is unchanged.
See [API Authentication](/docs/api/authentication) or
[x402 pay-per-request access](/docs/api/x402).
---
## July 17, 2026: Y2 Affiliate program
Every Y2 user can review the Affiliate Program in **Settings → Referrals**. A personal
FirstPromoter referral link is created and displayed only after the user explicitly accepts the
current program terms. The enrolled dashboard keeps provider credentials and raw customer details
server-side.
- The default Y2 Affiliate Program offers 15% recurring commission on eligible paid Lite and Pro
subscription revenue.
- Referral attribution converts only after a new customer completes a verified non-zero Lite or Pro
payment; a signup, 7-day trial, Free or Elite purchase, or fully discounted invoice does not
create a commissionable referral.
- A promoter must maintain an active paid Lite, Pro, or Elite workspace and keep their own Y2
payments current throughout commission eligibility, review, and payout.
- Pending commissions remain subject to the 60-day payment, refund, fraud, and eligibility review.
See the [Affiliate Program guide](/docs/affiliate-program) for enrollment, attribution, payout,
and troubleshooting details.
---
## July 10, 2026: Privacy-first Chat and connected intelligence
Y2 makes model selection clearer, keeps provider routing privacy-first, and connects Situation
Room signals to the wider intelligence workspace.
### What changed
#### Privacy-first Chat
- Y2 Chat and Agent Y2 route model requests through Zero Data Retention (ZDR) endpoints, with
provider data collection disabled. See [Chat Features](/docs/chat) and the
[Agent Y2 API guide](/docs/api/agent-y2).
- The model picker now shows readable names, provider filters, and search.
- GPT-5.6 Luna and NVIDIA Nemotron 3 Ultra are featured chat options, subject to current provider
availability.
#### Situation Room and connected intelligence
- The map now groups orbital and satellite layers, surveillance sources, live media, and expanded
infrastructure and military overlays in shared layer controls and a source catalog.
- The monitor tool rail places Intel Findings, Network Intel, ontology Search, and API Query beside
the map and loads each tool on demand.
- Live malware monitoring now includes relationship graphs for indicators, malware families, and
related entities.
- Search the Y2 ontology directly from the Situation Room to explore entities and their live
relationships.
- Map marker graphs now show richer source, confidence, time-range, and report context, with a
direct link to the related historical report when available.
- Monitor controls and data panels now adapt to smaller screens and simplify navigation.
#### Reliable scheduled reports
- Scheduled report generation now detects interrupted runs, queues bounded recovery attempts, and
keeps the profile active instead of delivering incomplete reports.
- Profile status and retry state are clearer when a report needs another attempt. See
[Scheduling](/docs/profiles/scheduling).
#### Clearer plan guidance
- Plan and branding documentation now matches current access: the full Situation Room is available
on Lite, Pro, and Elite, while reusable report branding is available on Pro and Elite. See
[Whitelabel Branding](/docs/profiles/branding).
---
## July 8, 2026: Agent Y2 API, OpenAI-compatible Chat, and MCP
Y2 now exposes Agent Y2 through API-key authenticated streaming, an OpenAI-compatible
chat-completions route, a standalone MCP server for agent clients, and refreshed OpenAPI,
client-integration, and endpoint documentation.
```mermaid
flowchart LR
client["Agent client"] --> native["Agent Y2 stream"]
client --> compatible["OpenAI-compatible stream"]
native --> executor["Shared Agent Y2 executor"]
compatible --> executor
executor --> tools["Scoped Y2 tools"]
```
### What changed
#### Agent Y2 API and OpenAI-compatible Chat
- Agent Y2 is now available through API-key authenticated streaming at
`POST /api/v1/agent-y2/chat/stream`; see the [Agent Y2 API guide](/docs/api/agent-y2)
and [generated Agent Y2 reference](/docs/api/reference/agent-y2).
- The OpenAI-compatible `POST /api/v1/chat/completions` route supports
`model: "y2-agent"` for streaming clients that already consume OpenAI-style chat
completion chunks.
- Both routes require the `agent:y2` API-key scope, use workspace-aware entitlements and
chat credit budgets, and attribute usage back to the correct workspace, thread, endpoint,
and API key.
- Agent Y2 API access is intentionally API-key only in this release, with a shared
`5/min`, `100/day` per-key throttle and a `10/min`, `250/day` workspace/user throttle
across both Agent Y2 API routes.
- OpenAI-compatible stateless chat calls preserve caller-provided transcript context when no
Y2 thread is attached, while threaded calls validate ownership before reserving chat
credits.
#### MCP, OpenAPI, and client documentation
`@y2-intel/mcp` is distributed separately from this platform repository. The maintained
[MCP guide](/docs/api/mcp) explains that boundary and shows how to inspect the capabilities of
the installed package version.
- New [Y2 MCP docs](/docs/api/mcp) cover the standalone `@y2-intel/mcp` stdio server for
Claude, Codex, Claude Desktop, and other MCP-compatible clients.
- The MCP server exposes Y2 docs and OpenAPI resources, bounded report and news tools, an
operation lookup tool, and `y2_ask_agent` for Agent Y2 access.
- The [OpenAPI specification](/docs/api/openapi), [Endpoint Reference](/docs/api/reference),
and [client guides](/docs/sdks) now reflect the canonical Agent Y2 routes, `agent:y2` scope,
request schemas, response codes, generated examples, and rate-limit headers.
- The [API Workbench](/docs/api/workbench) and docs navigation received sidebar, category,
route, theme-state, and 404 handling improvements for the expanded API surface.
#### Agent reliability and synthesis safety
- The shared Agent Y2 executor now powers both app-authenticated and API-key routes, keeping
the fixed Agent Y2 system prompt protected from caller override.
- Agent Y2 API responses hide internal reasoning chunks and align SDK examples to the
canonical `POST /api/v1/chat/completions` path.
- Degraded Y2 synthesis now fails closed instead of completing reports from low-confidence
output, with debugging documentation added for report-generation degradation triage.
---
# August 2026 Release Archive (/docs/changelog/2026-08)
[← Back to the latest release](/docs/changelog)
This archive preserves what changed in August 2026. Follow its links to the maintained feature
and API guides for current behavior.
## August 25, 2026: Native WebMCP support
Y2 now exposes native WebMCP tools inside the authenticated web app so compatible browser agents
can understand the current view and help users move through the product.
### Context-aware browser tools
- Agents can inspect the current authenticated Y2 view and navigate to advertised product areas.
- Project pages expose compact tools for listing and opening projects in the active workspace.
- InfoOps pages expose compact tools for listing and opening personal or subscribed briefings.
### Scoped by design
- WebMCP is registered only inside the authenticated app and uses existing workspace and profile
access controls.
- The initial tools cannot change account or intelligence data; open actions only navigate the
current tab.
- Unsupported browsers continue to use Y2 without any behavior change.
---
## August 22, 2026: MCP expands agent access
Y2 MCP 0.2.0 gives agents broader read access, live API discovery, and explicit controls for
actions that can change account data.
### Broader agent access
- The local MCP server now exposes 52 tools by default across reports, News Terminal, Situation
Room, Intel v2, profiles, Projects, Automations, webhooks, and public receipt lookup.
- Agents can inspect Y2 documentation and list API operations without an API key, then use scoped
keys for protected data.
- `y2_list_api_operations` and `y2_call_api` let agents discover and call endpoints from the live
OpenAPI contract when a dedicated typed tool is not available.
### Safer optional actions
- Profile, Project, Automation, webhook, and subscription-delivery mutations remain hidden unless
write tools are explicitly enabled and the API key has the matching scope.
- Agent Y2 remains disabled by default and requires a separate opt-in plus the `agent:y2` scope.
- Transient rate-limit and service failures are retried automatically before reaching the client.
See the [MCP setup guide](/docs/api/mcp), [authentication and scopes](/docs/api/authentication),
and [OpenAPI guide](/docs/api/openapi).
---
## August 7, 2026: Reliable API results, safer access, and updated plan limits
Y2 makes API results more complete and predictable, strengthens access checks around Automations,
and updates profile limits across Free, Lite, and Pro.
### More reliable API results
- Filtered list endpoints now continue through their underlying data until they can return a full
matching page or reach the end, preventing false-empty and incomplete pages.
- API errors use stable public codes and messages without exposing internal Convex details.
- Response validation and the API contract now agree across reports, projects, Automations,
profiles, webhooks, and Situation Room data.
- API Workbench responses preserve documented JSON, text, and audio behavior more consistently.
See the [API overview](/docs/api), [OpenAPI guide](/docs/api/openapi), and
[API Workbench](/docs/api/workbench).
### Safer Automations
- Automation runs revalidate workspace write access before reserving or spending credits. If access
changes during a run, Y2 stops the work and settles the reservation safely.
- API authorization failures now return the same status and error shape across Project and
Automation routes.
### Updated profile limits and Lite trial
- Pro supports up to **5 custom profiles**, up to **20 community or global subscriptions**, and a
**20-profile combined workspace total**.
- Lite continues to include **1 custom profile** and **3 community subscriptions**, does not include
reusable branding templates, and starts with a **7-day trial** during onboarding.
- Free supports **1 global or community profile total**. Elite limits and pricing are unchanged.
See [Plans and Pricing](/docs/plans) and [Creating Profiles](/docs/profiles/creating).
---
## August 5, 2026: Connected Explorer, Documents, and Project Evidence
Y2 makes private documents easier to organize, automatically connects report sources to Project
Evidence, and opens entity exploration with useful relationship context.
### Connected Explorer neighborhoods
- Opening an entity in Ad-hoc Explorer now loads a focused neighborhood of related entities and the
connections between them instead of leaving an isolated node.
- Selecting another entity expands around it while preserving the temporary graph, making it easier
to compare related intelligence before adding it to a Project.
- Y2 Copilot now works from the active temporary graph and selected entity, keeping its analysis
grounded in what is visible without treating the session as a saved Project.
- Clear loading and retry states distinguish a relationship lookup from an entity with no known
connections.
### Broader private document support
- Documents now supports PDFs, word-processing files, spreadsheets, presentations, ebooks, and
common text formats for private knowledge.
- PDFs retain page structure for more precise retrieval, while files that cannot be read reliably
return a clear processing status instead of incomplete content.
- A simplified Documents library shows where each file is available, its current status, and the
actions needed to manage or restore it.
### Report sources become Project Evidence
- Linking a profile to a Project now adds supported web sources from its existing reports to
Evidence and continues adding sources from future reports.
- Evidence keeps its connection to the originating report and source, making it easier to review the
material behind an investigation.
- Sync status and retry controls make it clear when sources are still being added or need another
attempt.
See the [Projects overview](/docs/projects) and
[organizing intelligence in Projects](/docs/projects/organizing-intelligence).
---
# September 2026 Release Notes (/docs/changelog/2026-09)
[← Back to the latest release](/docs/changelog)
## Upcoming
### More resilient report completion
- If one report-writing path cannot accept the required report format, Y2 continues through
supported alternatives, giving the report another chance to finish from the same collected
evidence.
- This applies to reports generated after the update. Reports that already ended in failure
remain unchanged and may still need to be retried.
### More reliable report and signup delivery
- A report no longer repeats its full generation cycle when a saved delivery channel has no
reachable contact details. Other recipients can continue receiving the report.
- Signup reminder emails can be prepared and queued without interrupting the scheduled reminder.
- An unreachable recipient still needs updated delivery details before they can receive a report.
Earlier failed reports and reminders are not sent again automatically.
### More dependable connected intelligence and Project Evidence
- Placeholder identifiers no longer interrupt connected-intelligence updates for unrelated
products or assets.
- Project Evidence can show lineage details for linked reports and other supported records.
- Existing identity conflicts still require review and are not combined automatically.
## September 17, 2026 private chats, conversation controls, and reliability
Start a temporary conversation or remove a saved one, with controls that fit the existing
desktop and mobile Chat layout. Updates also improve workspace switching, report retries,
and account deletion feedback.
### Private chats
- Choose **Private** at the top right of Chat to start a private conversation. Choose it again
to return to regular Chat.
- Private conversations stay out of your history and clear when you leave or reload the page.
- Private chats use workspace Agent Y2 credits at the same rates as regular chats, including
billable tool use. A reminder stays visible below the composer.
- Uploads, memory, and saved actions remain available in regular chats.
### Easier conversation cleanup
- Open a conversation's history menu or use the delete control in its header to delete it.
- Confirm before permanently removing the conversation and its messages. Credits already used
are not refunded.
- Long conversation titles leave room for the history menu, keeping its actions visible.
### Aligned mobile controls
- Private mode keeps its toggle at the top right, with a centered explanation and the composer
above the existing bottom navigation.
- Larger touch targets and safe-area spacing keep the new private-chat and deletion controls
easy to reach.
### Smoother updates and workspace changes
- Returning to an older open tab shows a reload notice when a newer version is available,
giving you time to save a draft before reloading.
- Switching accounts, workspaces, or plans clears requests from previous views. Workspace
pages wait for access to be ready before loading their content.
- Members can view their workspace's member list without requesting restricted invitation data.
- A failed command-menu preload no longer produces an unhandled error.
### More dependable reports and OSINT updates
- Retrying the same report-generation run reuses a saved report instead of creating a duplicate.
- Reports with missing workspace ownership stop before paid research begins, so the
configuration can be corrected before trying again.
- Conflicting entity identities can be reviewed while other OSINT records continue updating.
- CVE collection retries temporary service failures from the same page and time window.
### Clearer account deletion
- Account deletion shows actionable errors and confirms when a request has been accepted.
- A sign-out failure after an accepted request no longer appears as a failed deletion.
## September 14, 2026 Project intelligence PDF exports
Create a PDF from the materials saved to a Project, bringing findings, evidence, and visual
context together for review.
### One-page briefs and full reports
- Choose **Export PDF** from a Project to prepare a report.
- The default **one-page intelligence brief** leads with a synthesized bottom line, up to three
cited findings, implications, and what to watch next.
- **Full intelligence report** adds deeper analysis, uncertainties, selected supporting
passages, and visual context. Both formats summarize large Projects before rendering.
- Selection prioritizes recent, relevant evidence, including up to two recent reports per
linked profile. Older evidence is labelled as context; saved dates are separate from the
preparation date. Exports do not perform a live source refresh.
- Coverage notes identify sampled history, selected document passages, and unavailable content.
Original files, complete reports, and full conversation history remain in Y2.
- Selected conversation excerpts are optional in full reports and excluded by default.
- Synthesis uses workspace AI credits. If synthesis is unavailable, the PDF clearly identifies
its content as saved evidence excerpts instead of presenting a new assessment.
- Fixes a browser security policy error that could prevent either PDF format from generating.
- Brief and full exports share the same assessment while the dialog remains open and branding
and conversation settings stay unchanged. Full reports expand it with supporting analysis
and an evidence matrix. Reopen the dialog to prepare a new edition.
- Assessment generation distinguishes direct Project evidence from broader context, preserves
source families, and checks for repeated findings and elapsed milestones described as future events.
### Readable graphs and location maps
- Cited graphs combine known entity identities and select a connected explanation, with readable
node labels, types, directed arrows, and relationship keys. Unresolved aliases remain separate.
- Maps consolidate repeated event references under stable place keys. Distinct events at one
location remain separate in the key; repeated representations do not become extra locations.
- Regional and unknown geometry is labelled as context. Recorded points retain coordinates;
qualifying groups of nearby points can receive detail panels alongside the overview.
- Visual selection states material omissions. Missing facilities, coordinates, and finer map
detail are not invented; complete source graphs and records remain in Y2.
### Y2 or workspace branding
- Y2 branding is the default. Workspaces with branding enabled for their plan can select an
existing branding template, including its name, colors, logo, and disclaimer.
- PDFs include Project identity and source references so readers can return to the supporting
material in Y2.
- Visual branding does not replace the report's intelligence purpose with a template persona.
## September 12, 2026 update
Explore more of the observations available to Y2, give your team more room to work with Elite,
and move more easily between Chat and your investigations. Country cards also gain more dependable
news coverage and clearer geographic attribution.
### More complete OSINT map results
- The OSINT Monitor loads all stored observations matching your selected area, time range, and
event layers, removing the previous overall result cap.
- Loading progress shows how many observations have arrived and when the selection is complete.
If loading stops, retry from where it left off. If a refresh fails, the previous completed map
remains visible.
- Both globe and flat views group nearby observations into markers with counts. Clicking a group
opens its event list immediately; select an observation to view its location and details,
including when several observations share the same location.
- Switching to flat view downloads a smaller map renderer.
- Zooming and panning within a loaded region reuses its observations. Moving beyond that region
loads new coverage after you finish moving the map.
- Theater Intel keeps its regional headlines while you explore the map, with independent
refreshes that do not depend on zoom, panning, or visible map layers.
- Completed selections refresh automatically after five minutes, with a manual refresh option
when you want to check sooner.
### More dependable satellite fire observations
- Fire collection includes NASA FIRMS detections from Suomi NPP, NOAA-20, NOAA-21, and MODIS,
and removes the previous per-region observation cap.
- Observation times reflect the satellite acquisition time in UTC. Repeated collections of an
unchanged detection no longer make it appear newly updated.
- Clear warnings identify delayed or incomplete fire collection, even when all available map
observations have finished loading.
Map completion means all matching observations stored by Y2 have loaded. Fire collection still
covers the existing nine monitored regions, excludes low-confidence detections, and requests
today's and yesterday's observations. This update does not add global fire coverage or a
30-day fire history.
See [OSINT sources and coverage](/docs/osint/sources).
### More dependable country news
- Country cards select eligible news before filling their eight-headline list, so a surge in
satellite fire detections no longer hides available stories.
- Geopolitical and maritime RSS coverage better recognizes attacks, seizures, captures, and
shipping disruptions, including Red Sea escalation headlines.
- Country attribution gives priority to headline locations and specific places over countries
mentioned as sponsors or background context. Regional exposure remains distinct from where
an event occurred.
- Reused publisher identifiers no longer replace a different stored article, preserving its
identity and evidence links.
- Failed news requests show a retry option instead of an empty-news message.
The country-news API keeps its response fields and public observation IDs. After this update,
integrations with an older country-news pagination cursor must restart from the first page if
they receive an invalid or obsolete cursor response.
See [Country Intelligence Cards](/docs/osint/country-cards) and
[country-news API guidance](/docs/api/osint#read-country-news).
### More capacity for Elite teams
Elite's shared workspace allowances expand to **10× Pro** across research, publishing,
Automations, and API access.
| Allowance | Elite |
| --- | ---: |
| Agent Y2 credits per month | 10,000 |
| Custom profiles | 50 |
| Total profiles, including subscriptions | 200 |
| Automations | 100 |
| API keys | 100 |
| Default API requests per minute | 300 |
| Default API requests per day | 50,000 |
| Branding templates | 100 |
| Pronunciation dictionaries | 100 |
These allowances are shared across the organization workspace. Elite includes **five members**,
with larger organizations available by arrangement, at **$200/month or $2,000/year**. Situation
Room coverage, refresh frequency, and retention remain the same as Pro.
See [Plans and Limits](/docs/plans) and [Workspaces](/docs/workspaces).
### Easier sign-in and mobile navigation
- The blog listing loads less data, and links between documentation and articles navigate
more smoothly.
- Opening the Y2 home page while signed in takes you directly to Chat.
- Mobile tabs stay easier to reach across intelligence, developer, and settings views, with
fewer unexpected page jumps when switching tabs.
- Explorer gives mobile users clearer access to search and investigation controls.
### New guides for intelligence and AI workflows
New practical guides cover [OSINT with MCP](/blog/mcp-server-for-osint),
[scoped API keys for agents](/blog/mcp-scoped-api-key), and
[keeping useful context across agent sessions](/blog/your-ai-agents-memory-resets-every-session-thats-a-workflow-problem).
Learn how Y2 connects cyber, market, and global risk in the new
[guide to Y2's ontology](/blog/y2-ontology-cyber-market-global-tracking).
---
# Platform Changelog (/docs/changelog)
Follow product changes across intelligence coverage, Chat, API access, and platform operations.
## Upcoming
### More resilient report completion
- If one report-writing path cannot accept the required report format, Y2 continues through
supported alternatives, giving the report another chance to finish from the same collected
evidence.
- This applies to reports generated after the update. Reports that already ended in failure
remain unchanged and may still need to be retried.
### More reliable report and signup delivery
- A report no longer repeats its full generation cycle when a saved delivery channel has no
reachable contact details. Other recipients can continue receiving the report.
- Signup reminder emails can be prepared and queued without interrupting the scheduled reminder.
- An unreachable recipient still needs updated delivery details before they can receive a report.
Earlier failed reports and reminders are not sent again automatically.
### More dependable connected intelligence and Project Evidence
- Placeholder identifiers no longer interrupt connected-intelligence updates for unrelated
products or assets.
- Project Evidence can show lineage details for linked reports and other supported records.
- Existing identity conflicts still require review and are not combined automatically.
## Current release: September 17, 2026
Start a temporary conversation or remove a saved one, with controls that fit the existing
desktop and mobile Chat layout. Updates also improve workspace switching, report retries,
and account deletion feedback.
### Private chats
- Choose **Private** at the top right of Chat to start a private conversation. Choose it again
to return to regular Chat.
- Private conversations stay out of your history and clear when you leave or reload the page.
- Private chats use workspace Agent Y2 credits at the same rates as regular chats, including
billable tool use. A reminder stays visible below the composer.
- Uploads, memory, and saved actions remain available in regular chats.
### Easier conversation cleanup
- Open a conversation's history menu or use the delete control in its header to delete it.
- Confirm before permanently removing the conversation and its messages. Credits already used
are not refunded.
- Long conversation titles leave room for the history menu, keeping its actions visible.
### Aligned mobile controls
- Private mode keeps its toggle at the top right, with a centered explanation and the composer
above the existing bottom navigation.
- Larger touch targets and safe-area spacing keep the new private-chat and deletion controls
easy to reach.
### Smoother updates and workspace changes
- Returning to an older open tab shows a reload notice when a newer version is available,
giving you time to save a draft before reloading.
- Switching accounts, workspaces, or plans clears requests from previous views. Workspace
pages wait for access to be ready before loading their content.
- Members can view their workspace's member list without requesting restricted invitation data.
- A failed command-menu preload no longer produces an unhandled error.
### More dependable reports and OSINT updates
- Retrying the same report-generation run reuses a saved report instead of creating a duplicate.
- Reports with missing workspace ownership stop before paid research begins, so the
configuration can be corrected before trying again.
- Conflicting entity identities can be reviewed while other OSINT records continue updating.
- CVE collection retries temporary service failures from the same page and time window.
### Clearer account deletion
- Account deletion shows actionable errors and confirms when a request has been accepted.
- A sign-out failure after an accepted request no longer appears as a failed deletion.
[Read the September 2026 release notes →](/docs/changelog/2026-09)
## September 14, 2026
### Project intelligence PDF exports
Turn saved Project intelligence into a clean one-page brief or a full report, with supporting
graphs, location context, and consistent branding.
- **Lead with the bottom line:** the default one-page brief synthesizes recent evidence into
cited findings, implications, and what to watch, focused on your Project objective.
- **Handle large Projects:** recent report history and selected source passages are summarized
before PDF generation. Full reports add analysis, supporting excerpts, and visual context.
Coverage notes identify sampled history and unavailable content; originals remain in Y2.
- **Read one coherent assessment:** switch between brief and full formats using the same
assessment while branding and conversation settings stay unchanged. Full reports add
supporting analysis and a compact evidence matrix.
- **See meaningful connections and locations:** cited graphs combine known entity identities
and show relationship keys. Maps consolidate repeated event references by place, distinguish
regional context from recorded points, and retain stable location keys.
- **Choose your branding:** use Y2 branding by default or select a saved workspace branding
template when workspace branding is enabled for your plan.
- **Reliable browser exports:** fixes a browser security policy error that could prevent PDF
generation.
[Read about Project PDF exports in the September release notes →](/docs/changelog/2026-09#september-14-2026-project-intelligence-pdf-exports)
### Y2 desktop apps
This release introduces Y2 Desktop for macOS and Windows, bringing Chat, Projects, reports, and
your intelligence workspace into a dedicated app.
Public installer downloads remain unavailable until signing and installation checks are complete.
Mac downloads are deferred pending Apple signing and notarization.
[Read the desktop app guide →](/docs/apps) · [Check download availability →](/download)
## September 12, 2026
### More complete monitoring and higher Elite limits
This update brings more observations and country news into the OSINT Monitor, expands Elite
workspace allowances, and improves navigation across Chat, documentation, and the blog.
- **Explore more observations:** load all stored observations matching your selected map area,
time range, and event layers, with clear progress, retry controls, and grouped markers that
let you inspect individual observations.
- **Keep regional context in view:** Theater Intel headlines stay available while you pan, zoom,
or change map layers, and grouped markers open their observation lists directly.
- **Find relevant country news:** satellite fire detections no longer crowd headlines out of
country cards. Improved geographic attribution and escalation coverage help connect stories
to the right country, with a retry option when news cannot load.
- **Follow satellite fire activity:** expanded NASA FIRMS collection includes more satellite
products, more accurate observation times, and clearer warnings when collection is incomplete.
- **Do more with Elite:** shared workspace allowances expand to 10× Pro for Agent Y2 credits,
profiles, Automations, API access, branding templates, and pronunciation dictionaries.
- **Move through Y2 more easily:** signed-in home visits open Chat, and mobile navigation keeps
tabs and Explorer controls easier to reach. Documentation and blog links navigate more smoothly.
- **Understand connected intelligence:** a new [guide to Y2's ontology](/blog/y2-ontology-cyber-market-global-tracking)
explains how cyber, market, and global-risk observations connect through shared entities and evidence.
[Read the September 2026 release notes →](/docs/changelog/2026-09)
## Report and intelligence reliability
These changes improve report retries, source imports, news updates, and
incident details, with clearer context to help support teams investigate failed reports.
### More resilient report retries
- Keeps already accepted research when a temporary report-writing problem triggers a retry,
avoiding repeat source collection and giving the report another chance to finish from the same
evidence.
- This applies to reports started after the update; reports already underway continue with their
existing recovery behavior.
### Reliable Project Evidence
- Fixes a problem that can prevent report sources from being added to Project Evidence.
### More dependable Situation Room updates
- Reduces interruptions when several news items update the same incident, helping related
coverage stay connected.
### Reliable incident details
- Restores incident details when a location includes normalized geographic identifiers, keeping
affected Situation Room and API views available.
### Better support for report failures
- Gives support clearer context about the affected profile and workspace when a report fails,
without adding report prompts or content to these error details.
## September 1, 2026
Y2 Elite workspace owners and admins can now see how activity and credit consumption are
distributed across their entire organization workspace.
### Workspace usage at a glance
- Governance shows one shared Agent Y2 credit balance for the subscription-aligned credit period.
- Informational Activity units remain grouped into API, research, deliveries, and AI for the
selected UTC month; they are separate from the enforced Agent Y2 credit allowance.
- Member breakdowns attribute both Agent Y2 credit consumption and Activity units without creating
separate member allowances.
### Accountable API key activity
- Every workspace API key shows its responsible member, active or revoked state, request volume for
the current month, and latest use.
- Request totals now come from recorded API activity, resolving keys that previously appeared to
have zero calls.
- Seat limits, API key capacity, the Agent Y2 credit ledger, Activity units, and workspace audit
history remain together in one administrative view.
## Previous releases
Earlier release notes are grouped into monthly archives. Each link opens the full release entry.
### August 2026
- [August 25: Native WebMCP support](/docs/changelog/2026-08#august-25-2026-native-webmcp-support)
- [August 22: MCP expands agent access](/docs/changelog/2026-08#august-22-2026-mcp-expands-agent-access)
- [August 7: Reliable API results, safer access, and updated plan limits](/docs/changelog/2026-08#august-7-2026-reliable-api-results-safer-access-and-updated-plan-limits)
- [August 5: Connected Explorer, Documents, and Project Evidence](/docs/changelog/2026-08#august-5-2026-connected-explorer-documents-and-project-evidence)
[View the complete August 2026 archive →](/docs/changelog/2026-08)
### July 2026
- [July 30: Privacy, product platform, and installed-app foundation](/docs/changelog/2026-07#july-30-2026-privacy-product-platform-and-installed-app-foundation)
- [July 24: Projects, Automations, and workspace governance](/docs/changelog/2026-07#july-24-2026-projects-automations-and-workspace-governance)
- [July 21: Global coverage, Chat updates, and Pro API keys](/docs/changelog/2026-07#july-21-2026-global-coverage-chat-updates-and-pro-api-keys)
- [July 17: Y2 Affiliate Program](/docs/changelog/2026-07#july-17-2026-y2-affiliate-program)
- [July 10: Privacy-first Chat and connected intelligence](/docs/changelog/2026-07#july-10-2026-privacy-first-chat-and-connected-intelligence)
- [July 8: Agent Y2 API, OpenAI-compatible Chat, and MCP](/docs/changelog/2026-07#july-8-2026-agent-y2-api-openai-compatible-chat-and-mcp)
[View the complete July 2026 archive →](/docs/changelog/2026-07)
### June 2026
- [June 30: News Terminal, Lite trial signup, and platform reliability](/docs/changelog/2026-06#june-30-2026-news-terminal-lite-trial-signup-and-platform-reliability)
- [June 18: Chat reliability, OSINT Monitor, and landing performance](/docs/changelog/2026-06#june-18-2026-chat-reliability-osint-monitor-and-landing-performance)
- [Y2 Elite B2B, Organization Workspaces and Intel API v2](/docs/changelog/2026-06#june-2026-y2-elite-b2b-organization-workspaces-and-intel-api-v2)
[View the complete June 2026 archive →](/docs/changelog/2026-06)
### March 2026
- [Y2SOURCE Event Provenance Engine](/docs/changelog/2026-03#march-2026-y2source-event-provenance-engine)
- [Profile Sharing Defaults](/docs/changelog/2026-03#profile-sharing-defaults)
- [Situation Room Map Enhancements](/docs/changelog/2026-03#situation-room-map-enhancements)
[View the complete March 2026 archive →](/docs/changelog/2026-03)
---
# Intelligence Chat (/docs/chat)
Y2 Intelligence Chat combines a selectable language model with platform tools for research,
documentation, news, profiles, and intelligence data. Chat is available in active Lite, Pro, and
Elite workspaces.
```mermaid
flowchart LR
A[Your message] --> B[ZDR-eligible model]
B --> C{Tool needed?}
C -->|No| D[Stream response]
C -->|Yes| E[Entitled Y2 tool]
E --> B
D --> F[Store thread and usage]
```
## Start a conversation
### Open Chat
Select **Chat** in the signed-in app. Free workspaces see an upgrade wall because their chat
credit limit is zero.
### Select a model
Open the model selector and choose an available model. The list is populated from Y2's current
ZDR-eligible catalog, so names and availability can change between visits.
### Describe the outcome
Ask a direct question or use a quick action for web search, InfoOps setup, topic research, or
Y2 documentation.
### Review tool activity
The thread displays tool activity and structured result cards when the model searches data or
proposes a profile change. Verify consequential results before confirming a write action.
Press Enter to send and Shift+Enter for a new line. While a response is streaming, use the stop
control to end generation. You can change the model for an existing thread or regenerate the last
assistant response.
## Model routing and privacy
The selector returns only model records marked as supporting Zero Data Retention. At request time,
Y2 also asks OpenRouter to deny provider data collection, require compatible request parameters,
and route only to ZDR endpoints.
ZDR applies to external model routing. Y2 still stores chat threads, messages, tool results,
workspace context, attachments when used, and usage records to operate the product. Tools such
as web search or image generation also send the necessary prompt or query to their configured
service.
If a saved model is no longer ZDR-eligible, Y2 falls back to an eligible default when one exists;
otherwise thread creation or model selection fails with a model-unavailable error.
## Available tools
Tools are attached to a request according to the active workspace's entitlements and the selected
model's capabilities.
Searches the local Y2 documentation corpus for product, API, profile, and delivery questions.
This tool is present in standard chat requests.
Searches current external sources when recent information is needed. It is enabled for Lite,
Pro, and Elite chat.
Searches cached News signals, filters by text or sentiment, retrieves available recaps, and
lists the current 40-feed catalog. Longer recap timeframe identifiers can be requested, but
the current News registry schedules only 12-hour recaps.
Searches shared/community profiles and can subscribe after confirmation. When the workspace
can create custom profiles, Chat can also collect profile fields, create a profile, inspect an
existing profile, preview changes, and update it. Normal profile and subscription limits still
apply.
Entitled paid workspaces can query Situation Room data. The current tool set includes OSINT
search, entities, incidents, cyber intelligence, FININT, markets, investigations, and
asynchronous investigation enrichment where the corresponding feature is enabled.
Paid workspaces can generate images. A dedicated image tool is attached when the selected
chat model does not provide native image generation; native-capable models handle the request
directly. Generated images use their own request-scoped hold in the same organization credit
ledger.
A zero-cost internal tool can still be part of a metered chat response. Model input and output
tokens consume Agent Y2 credits even when the tool itself adds no external-service charge.
## Write actions and confirmation
Some tools can change Y2 data:
- Follow a public or community profile.
- Create a custom InfoOps profile.
- Update profile settings or status.
- Start investigation enrichment.
The profile flows are designed to search the shared library first, preview proposed settings or
changes, and wait for explicit confirmation before creating or updating. Read the structured card
carefully, especially the profile, schedule, active workspace, and community-sharing setting.
## Agent Y2 credits
One Agent Y2 credit represents $0.01 of normalized usage. The active workspace has one shared
monthly pool: members, API keys, Automations, and tools do not receive separate allowances. Y2
meters model input and output using the
selected model's pricing data or a fallback pricing tier when provider pricing is absent. Certain
tools add usage, while internal cache reads and profile mutations currently add no separate tool
charge.
| Plan | Monthly Agent Y2 allowance | Agent Y2 access |
| --- | ---: | --- |
| Free | 0 credits | No |
| Lite | 100 credits per month | Yes |
| Pro | 1,000 credits per month | Yes |
| Elite | 10,000 credits per month | Yes, with workspace governance views |
The balance reports the allowance, settled credits, active request holds, available credits, exact
period boundaries, and next replenishment time. Organization governance also attributes settled
usage and active holds to members and request sources without creating per-member allowances.
### Billing periods
Monthly subscriptions use the active Stripe subscription boundaries. Annual subscriptions still
replenish the monthly allowance each month, anchored to the UTC date and time when the annual term
began. Payment cadence never multiplies the allowance. If no active paid-subscription period is
available, credits use UTC calendar months.
Before provider work begins, Y2 creates a bounded, request-scoped hold. Settling converts only that
request's actual cost into usage; failures release its hold; expired holds stop reducing available
credits. At 80% committed usage (settled plus active holds) the UI warns, at 95% it shows a critical
warning, and at 100% it disables new messages until replenishment.
## Organize and remove conversations
Chat stores threads for the signed-in user and loads recent history in the sidebar. Threads can be
associated with a Y2 project, renamed, switched to another eligible model, and deleted.
Deleting a thread also deletes its stored chat messages. Treat deletion as permanent and export
or copy anything you need first.
## Troubleshoot Chat
Confirm the active workspace is Lite, Pro, or Elite. Entitlements follow the active workspace,
not just the signed-in user's other subscriptions.
The selector includes only models currently stored as ZDR-supported. Choose another available
model; a featured or previously saved model can disappear when eligibility changes.
Check the shared workspace credit meter. At 100% of the current credit-period allowance, new messages are
blocked until the period resets.
Tool availability depends on the active workspace entitlement, profile limits, and selected
model capabilities. Switch workspaces or review plans when the requested feature is not
enabled.
Try another eligible model or a smaller request. Provider credit, rate, capability, or routing
failures can occur even when Agent Y2 credits remain.
## Next steps
Stream the agent from an application with a scoped API key.
Review every field available in the profile editor.
Inspect the cache that powers Chat's news tool.
---
# Contribute to Y2 (/docs/community/contributing)
Choose the contribution path that matches what you want to improve. Product and documentation
feedback goes to Y2; public briefing profiles are published inside the application; code changes
belong in the repository that owns the relevant SDK or tool.
## Choose a contribution path
| Your goal | Use this path | What to include |
| --- | --- | --- |
| Publish a reusable briefing | Enable **Share with Community** on a workspace-owned profile | Clear topic, safe public instructions, useful tags, and reviewed output |
| Report a product or documentation problem | Use PostHog Support in the desktop app or email [support@y2.dev](mailto:support@y2.dev) | Route or docs URL, expected result, actual result, and reproduction details |
| Report unsafe or inaccurate public content | Email [support@y2.dev](mailto:support@y2.dev) | Profile name or URL, the specific concern, and supporting context |
| Build an integration | Use the OpenAPI contract, API keys, and webhooks | Least-privilege scopes, signature verification, retries, and idempotency |
| Change an SDK or CLI | Follow the contribution and license terms in that tool's own repository | Focused change, matching tests, documentation, and compatibility notes |
## Publish a community briefing
Community sharing is the only in-product publication path documented here. It makes the original
profile and its later reports discoverable to other signed-in Y2 users; it does not submit the
profile to a review queue.
### Prepare a focused profile
Use a clear name, topic, report structure, and compact set of tags. Generate and review at least
one report before asking others to rely on it.
### Remove private context
Review the custom instructions, sources, sections, and generated output. Remove credentials,
customer data, internal names, and confidential research requirements.
### Enable sharing
Open Advanced Configuration, enable **Share with Community**, and save the profile.
### Verify the public result
Open [InfoOps → Discover](/app/infoops/community), search for the profile, and inspect its
details as a reader would.
See [Share a Profile](/docs/profiles/sharing) for the complete visibility and subscriber model.
## Report a product or documentation problem
The desktop application loads PostHog Support when analytics consent is enabled. The widget is
hidden on mobile and while Copilot is open. Email [support@y2.dev](mailto:support@y2.dev) when the
widget is unavailable or the report needs a private channel.
A useful report contains:
- The application route or documentation URL.
- The active workspace type and plan when the behavior is entitlement-related.
- A short, repeatable sequence of actions.
- The expected and actual result.
- Relevant timestamps, request IDs, HTTP status codes, or screenshots.
- Whether the issue is consistent or intermittent.
For a documentation correction, quote only the smallest inaccurate statement and point to the
current UI, API response, or other evidence that contradicts it.
Never include API keys, webhook signing secrets, one-time authentication codes, payment details,
or unredacted personal data. Redact authorization headers and sensitive payload fields before
attaching logs or screenshots.
## Report a security concern privately
Send a security concern to [support@y2.dev](mailto:support@y2.dev). Include the affected surface,
impact, reproduction conditions, and a safe proof of concept. Do not publish exploit details,
credentials, or another workspace's data in a community profile or public issue.
Y2's local product repository does not define a separate public security-policy file, so this page
does not promise a public issue tracker, bounty, response time, or disclosure schedule.
## Build an integration
Use the documented API contract rather than depending on application internals.
Generate clients from the same specification used by the endpoint reference
Create tenant-scoped keys and grant only the required scopes
Verify signatures and design receivers for retries and duplicate delivery
Start from task-oriented API patterns
There is no documented public integration marketplace or automatic submission process in the
current application. Contact support before presenting an integration as endorsed by Y2.
## Work on an SDK or CLI
The SDK pages link to their current source and package locations. Before changing a tool, open its
own repository and verify its README, contribution instructions, tests, release process, and
license. Those repository-local rules take precedence over this general guide.
Review the TypeScript package, examples, and linked source repository
Review the Python package, examples, and linked source repository
Review CLI installation, commands, and linked source repository
The Y2 platform repository identifies itself as proprietary, and the API contract uses
proprietary terms. Do not assume that the platform, documentation, examples, and external SDK
repositories share one license. Check the license attached to the exact artifact you plan to use
or modify.
## Contribution checklist
- Keep the change focused on one user problem.
- Verify examples against the current API contract or application behavior.
- Include tests when the target repository has a test suite.
- Update the documentation affected by a behavior or interface change.
- Avoid promising badges, editorial review, promotion, credits, or response times unless Y2 has
confirmed them for the specific contribution.
- Use [@y2_intel on X](https://x.com/y2_intel) for public updates; use support channels for
actionable reports.
---
# Community (/docs/community)
Y2 Community is the public briefing catalog inside **InfoOps → Discover**. It combines profiles
curated by Agent Y2 with profiles that users have marked **Share with Community**.
This catalog is about reusable InfoOps profiles and their reports. It is separate from the
“emergent signals” extracted inside reports and intelligence APIs.
## How community profiles work
The profile remains owned by its creator. Following it creates an active subscription in your
current workspace; it does not copy the profile or let you edit the creator's research settings.
## What you can do
Browse global, city, industry, and user-published profiles; filter the catalog; and follow a
briefing
Make a profile and its generated reports discoverable to other Y2 users
Review subscriptions and configure delivery from My Briefings
Report a problem, improve documentation, or build against the documented API
## Catalog organization
Discover groups public profiles using their stored flags and tags:
| Browse group | Current rule |
| --- | --- |
| Featured | Agent Y2 global profile without a `city` or `industry` tag |
| Cities | Any public profile tagged `city` |
| Industries | Any public profile tagged `industry` |
| Public | User-shared profile that is not classified as city or industry |
These are browse rules, not editorial certifications. Tags influence where a profile appears.
The catalog also supports search across name, topic, custom prompt, and tags; filters for public
type, subscription status, profile status, tags, audio, and saved recursion depth; and sorting by
recent delivery, name, creation date, or subscriber value.
## Subscription limits
Subscriptions are counted in the active workspace.
| Plan | Public-profile capacity |
| --- | --- |
| Free | 1 global or community subscription |
| Lite | 3 global or community subscriptions |
| Pro | Up to 20 total profiles, including up to 5 custom profiles and community subscriptions |
| Elite | Up to 200 total profiles, including up to 50 custom profiles and community subscriptions |
The subscription counts even if delivery is paused. Unfollow a profile from Discover or
My Briefings to remove its subscription record and free capacity.
## Public-data boundary
When a creator enables **Share with Community**, the profile becomes discoverable immediately; the
current application path does not create a moderation or approval queue. Other signed-in users can
inspect the profile topic, creator label, schedule and research configuration, subscriber and report
counts, and available generated reports.
Review the profile's topic, custom prompt, tags, configuration, and generated reports before
sharing. Community visibility is a publication control, not private team sharing.
## Quality and reporting
Y2 displays activity, report count, subscriber count, creator, tags, and configuration details, but
the current code does not assign a “Verified Analyst” badge or guarantee content review. Evaluate a
profile's topic and latest report before following it.
To report inaccurate, inappropriate, or unsafe public content, email
[support@y2.dev](mailto:support@y2.dev) with the profile name or URL and a concise description.
## Related guides
Browse, filter, inspect, follow, and unfollow public briefing profiles
Understand exactly what becomes public and how to turn sharing off
Compare profile capacity and delivery features
---
# Browse Public Briefings (/docs/community/signals)
Use **InfoOps → Discover** to find public briefing profiles and follow them in your active
workspace. The catalog includes Agent Y2 global profiles and profiles that users have enabled for
community sharing.
This page covers the briefing catalog. It is separate from the signal objects returned by the
intelligence APIs.
## Before you follow a profile
Check the workspace switcher first. A follow belongs to the active workspace and consumes that
workspace's profile capacity; switching workspaces later does not move the subscription.
| Plan | Capacity used by public briefings |
| --- | --- |
| Free | Up to 1 global or community subscription |
| Lite | Up to 3 global or community subscriptions |
| Pro | Subscription counts toward the 20-profile total |
| Elite | Subscription counts toward the 200-profile total |
Pausing delivery does not free capacity. Unfollow the profile to remove its subscription record.
## Find and follow a briefing
### Open Discover
In the active workspace, open [InfoOps → Discover](/app/infoops/community).
### Choose a browse group
Use **All**, **Featured**, **Cities**, **Industries**, or **Public**. With no search or tag
filters, the All view shows grouped previews and up to 12 profiles per group. Choose **View
All** when a group contains more results.
### Narrow the catalog
Search by profile name, topic, custom instructions, or tags. Then combine the type, status,
audio, saved depth, tag, and sort controls as needed.
### Inspect the profile
Open the details view before following. Review the creator, topic, instructions, cadence,
source and model configuration, activity status, latest report, and audio setting. Report and
subscriber counts appear when available.
### Follow in the active workspace
Select **Follow**. Y2 creates a workspace-scoped subscription to the original profile; it does
not clone the profile or grant permission to edit it.
## How profiles are grouped
Discover derives its groups from stored profile flags and tags. The labels are browse rules, not
editorial certifications.
| Group | Current catalog rule |
| --- | --- |
| Featured | Agent Y2 global profile without a `city` or `industry` tag |
| Cities | Public profile tagged `city` |
| Industries | Public profile tagged `industry` |
| Public | User-shared profile not classified as city or industry |
A `city` or `industry` tag takes precedence over the Featured grouping, even when the profile is
also global.
## Search, filter, and sort
| Control | Available behavior |
| --- | --- |
| Search | Matches name, topic, custom instructions, and tags; spaces, slashes, and hyphens are normalized for compact matching |
| Type | All, Featured/global, or Public/user-shared |
| Subscription | All, Following, or not currently followed through the status controls |
| Profile status | All, Active, or Inactive |
| Audio | Any, has audio enabled, or has no audio |
| Saved depth | Quick for no saved depth or `0`; Standard for `1–2`; Deep for `3+` |
| Tags | Matches a profile when it has at least one selected tag |
| Sort | Alphabetical, most recent delivery, most followed, or recently created |
The depth badge describes the profile's saved configuration. Report execution currently caps
recursive research at depth 1; see [Research Workflows](/docs/workflows) for the runtime behavior.
Subscriber counts are available in profile details, but the current catalog-list payload does
not guarantee a count for every card. Profiles without a value can tie in **Most Followed**
ordering, so inspect the profile and its latest report instead of treating the sort as a quality
ranking.
## What following changes
Following a profile:
- Adds one subscription record to the active workspace.
- Makes the profile available from **My Briefings**.
- Preserves the creator's ownership and research configuration.
- Lets the subscriber use delivery controls supported by their plan.
- Receives later reports from the same shared profile while the subscription remains active.
The creator can change the profile or stop sharing it. Existing subscription records are not
automatically cloned or converted into workspace-owned profiles.
To free capacity, choose **Following** in Discover or open **My Briefings**, then unfollow the
profile. Turning a subscription inactive only pauses it and still counts toward the limit.
## App and API boundary
Use the application to browse the public catalog and create or remove community subscriptions.
The public [`GET /profiles`](/docs/api/reference/profiles/listProfiles) endpoint lists profile
subscription records accessible to the authenticated workspace; it is not a public-catalog search
endpoint.
Use the generated [Profiles API reference](/docs/api/reference/profiles) for the current request,
response, authentication, and scope contract.
## Troubleshooting
Confirm the active workspace and its plan. Free and Lite have dedicated public-subscription
limits; Pro and Elite combine owned profiles and followed profiles in one total. Unfollow an
unused profile to free capacity.
Clear the search, selected tags, and type or status filters. Then return to **All**. Also verify
that you did not switch workspaces when checking which profiles are already followed.
Inspect its details and latest report before following. The catalog can display inactive public
profiles; visibility does not guarantee a future publishing cadence.
Following references the creator's profile. Create a new workspace-owned profile when you need
different instructions, sources, cadence, or sections.
## Related guides
Review catalog visibility, quality signals, and subscription limits
Publish a workspace-owned profile and understand what becomes visible
Create and maintain workspace-owned and followed briefings
Check profile, delivery, API, and workspace limits
---
# Email Delivery (/docs/delivery/email)
Email is the default delivery method for an Information Operations subscription. Y2 renders the
generated report as an HTML email and sends it to the subscription owner's current account email.
## Configure email for a profile
Delivery preferences belong to a profile subscription. Changing one profile does not change the
others.
### Open My Profiles
Go to **InfoOps → My Profiles**.
### Open Delivery Preferences
Open the profile card's actions menu, then select **Delivery Preferences**. You can also find
the same controls under **Advanced** when editing an existing profile.
### Select Email Only
Choose **Email Only**. If a stored subscription has no delivery method, Y2 also treats it as
email.
### Choose the audience
Keep **Individual** to send to your account email. Eligible Elite workspaces can select
**Workspace** to include workspace members.
### Save preferences
Select **Save Preferences**. Saving also makes the subscription active.
A new profile must be created before its subscription-level delivery preferences are available.
Until then, the default method is email to the individual account.
## Verify or change the recipient email
Email delivery uses the email stored on your Y2 account at the time the report is sent.
1. Open **Settings** in the app.
2. Find the **Email** account section.
3. Confirm the current verified address.
4. To change it, enter the new address and complete the emailed one-time-code flow.
After verification, subsequent individual deliveries use the updated address. Y2 does not expose
an arbitrary recipient-address field on each profile.
## Individual and workspace audiences
| Audience | Availability | Recipients |
| --- | --- | --- |
| Individual | All plans | Subscription owner's current account email |
| Workspace | Elite workspaces with member support | Owner plus eligible non-suspended members with account emails |
The workspace audience is bounded by the workspace's member entitlement. A missing email on a
member record prevents delivery to that member.
Workspace delivery can expose the complete report to other members. Confirm the profile topic,
report content, and workspace membership before enabling it.
```mermaid
flowchart LR
A[Scheduled profile] --> B[Report generated]
B --> C{Delivery preference}
C -->|Email| D[Account email]
C -->|Email + SMS| D
C -->|Elite workspace audience| E[Eligible workspace members]
```
## Email contents
The current report template can include:
Profile name, topic, generated report body, and available intelligence sections.
Sanitized citations from the report or a source-link section when separate citations are not
already present.
A link to the in-app report page when audio narration exists. Audio is not attached.
Assigned logo, colors, terminology, and before/after static content when configured.
The email subject is the profile name. The preview text uses the configured branding prefix plus
that name. Report and branding HTML are sanitized before rendering.
See [Branding Templates](/docs/profiles/branding) for the fields that can change email appearance
and copy.
## Combine delivery channels
The stored delivery method is one of four values:
There is currently no combined Email + Webhook option. Select **Webhook** only when the endpoint
should replace the email channel for that subscription.
## Delivery timing and history
Y2 attempts delivery after report generation reaches the delivery stage. Generation duration,
queueing, retries, and the email provider affect arrival time; there is no fixed five-minute
delivery guarantee.
Open **InfoOps → History** to inspect the recorded date, profile, status, method, and delivery
details. A successful send request means Y2 queued or sent the message through its email provider;
the recipient's provider can still filter, defer, bounce, or reject it.
## Troubleshoot missing email
Confirm that the profile and subscription are active, then review its next scheduled run. A
delivery cannot occur until report generation succeeds.
Open **Delivery Preferences** and confirm that the method is **Email Only** or **Email + SMS**.
**SMS Only** and **Webhook** do not send the report email.
Open **Settings → Email**, complete verification if needed, and confirm the address before the
next delivery.
Review the delivery details, correct the account or workspace-member email, and wait for the
next run. For persistent failures, contact [support@y2.dev](mailto:support@y2.dev).
Check spam, quarantine, inbox rules, and the receiving provider's logs. Allow the exact sender
shown in a received Y2 message; the configured sender address can vary by environment.
## Next steps
Add a verified-phone summary channel on Pro or Elite.
Replace email delivery with an authenticated endpoint.
Understand when profile generation begins.
---
# SMS Delivery (/docs/delivery/sms)
SMS delivery sends an intelligence-report summary to the subscription owner's verified phone.
It is available to active Pro and Elite workspaces.
SMS requires **Pro or Elite**, a verified account phone, and a profile delivery preference set
to **SMS Only** or **Email + SMS**.
```mermaid
flowchart LR
A[Verified account phone] --> B[Profile subscription]
B --> C[Report generated]
C --> D{SMS summary exists?}
D -->|Yes| E[Use generated summary]
D -->|No| F[Extract bounded report summary]
E --> G[SMS provider]
F --> G
```
## Add and verify a phone
### Open account settings
In the app, go to **Settings → Account** and find **Phone**.
### Enter the number
Select a country code, enter the local number, and request a verification code. The current
input supports common country-code presets and stores the combined international number.
### Verify ownership
Enter the six-digit code sent to that phone. SMS delivery remains unavailable until the
account has a phone verification timestamp.
Use a number that can receive SMS. Standard carrier message and data rates may apply.
## Enable SMS for a profile
### Open Delivery Preferences
Go to **InfoOps → My Profiles**, open the profile card's actions menu, and select **Delivery
Preferences**.
### Choose a method
Select **SMS Only** to omit email, or **Email + SMS** to receive both channels.
### Save
Select **Save Preferences**. The backend rechecks the active workspace entitlement and phone
verification before accepting an SMS method.
| Method | Email | SMS |
| --- | --- | --- |
| SMS Only | No | Yes |
| Email + SMS | Yes | Yes |
SMS is sent only to the subscription owner's account phone. The Elite **Workspace** audience
setting applies to email recipients, not to member phone numbers.
## Message contents
Y2 first uses the report's pre-generated `smsSummary` when present. If a report does not have one,
the delivery path extracts a concise section from the HTML report and prefixes it with the profile
name.
```text
Y2 Cyber Risk:
Critical infrastructure advisories increased across the monitored sources...
```
The fallback formatter targets a 140-character message and truncates its extracted summary when
needed. Generated `smsSummary` content is also instructed to be concise, but carrier segmentation
can vary with message length and character encoding.
The current SMS template does not guarantee a link to the full report. Open Y2 directly when
you need the report body, citations, or audio.
## Timing, failures, and limits
SMS is attempted after report generation enters the delivery stage. When **Email + SMS** is
selected, the current workflow attempts email before SMS. Queueing and provider response times
vary, so there is no fixed one-minute arrival guarantee.
The send provider can reject a request for reasons including rate limits, an invalid number or
message, authentication, or service availability. Y2 records SMS success or failure in
**InfoOps → History** when the provider call reaches the SMS delivery action.
Y2 does not currently document a fixed per-account daily SMS quota. Plan access and provider
limits still apply.
## Change or remove the phone
Open **Settings → Account → Phone** to change or remove the verified number.
- Changing the number requires a new one-time-code verification.
- Removing it clears the phone and verification timestamp from the account.
- Removing a phone prevents later SMS delivery, but it does not rewrite every stored profile
preference. Change affected profiles to **Email Only** if you still want delivery.
The SMS provider necessarily receives the destination number and message content to deliver the
text. Do not place secrets or highly sensitive material in profile instructions that can appear in
a report summary.
## Troubleshoot SMS delivery
Confirm that the active workspace is Pro or Elite and that **Settings → Account → Phone**
shows a verified number.
Confirm the selected country code and local number, then retry. Verification requests can fail
when the SMS provider is unavailable or the number is already associated with another account.
Verify that the profile uses **Email + SMS**, not **Email Only**. Then review **InfoOps →
History** for an SMS-specific failure.
Add and verify a phone again, then reopen Delivery Preferences. Stored SMS selections cannot
deliver without a current verified account phone.
Check the History details and retry on the next report run. For persistent authentication or
service failures, contact [support@y2.dev](mailto:support@y2.dev).
## Next steps
Receive the complete HTML report and source links.
Deliver structured events to an endpoint instead of email or SMS.
Compare delivery features across workspace plans.
---
# Webhook Delivery (/docs/delivery/webhooks)
Webhook delivery sends a signed CloudEvents 1.0 notification to an HTTPS endpoint after a report
is published. It is available to active Lite, Pro, and Elite workspaces.
```mermaid
flowchart LR
A[Report published] --> B[CloudEvent persisted]
B --> C[Signed POST]
C --> D{Endpoint returns 2xx?}
D -->|Yes| E[Success recorded]
D -->|No| F[Failure count incremented]
F --> G[Disabled after 5 consecutive failures]
```
A webhook notification is compact. It identifies the report and provides API links; it does
not embed the full HTML report or all source records.
## Create an endpoint in Y2
You do not need an API key when configuring webhooks in the app.
### Open Webhooks
In an active paid workspace, go to **Developers → Webhooks**.
### Create a configuration
Enter a name and public endpoint URL. Production webhook URLs must use HTTPS. Localhost,
loopback, private-network, link-local, and other blocked addresses are rejected.
### Add verification and authentication
Generate or enter an optional signing secret. You can also add custom string headers, such as
an authorization token for your endpoint.
`Host`, `Content-Length`, and `Content-Type` cannot be supplied as custom headers. Y2's own
delivery headers take precedence over custom values.
### Save and test
Create the webhook, then select **Test Webhook**. The test sends a
`dev.y2.webhook.test.v1` CloudEvent and waits up to 10 seconds for a response.
### Assign it to a profile
Go to **InfoOps → My Profiles → Delivery Preferences**, choose **Webhook**, select the active
configuration, and save.
Selecting **Webhook** replaces email and SMS for that subscription. There is no combined
Email + Webhook delivery method.
## Receive a report event
Report deliveries use `Content-Type: application/cloudevents+json` and the event type
`dev.y2.report.generated.v1`.
```json
{
"specversion": "1.0",
"id": "y2mexampleeventid",
"source": "https://api.y2.dev/api/v1/profiles/prf_0123456789abcdef01234567",
"type": "dev.y2.report.generated.v1",
"subject": "reports/rpt_0123456789abcdef01234567",
"time": "2026-07-21T12:00:00.000Z",
"datacontenttype": "application/json",
"dataschema": "https://api.y2.dev/schemas/events/report-generated-v1.json",
"data": {
"report": {
"id": "rpt_0123456789abcdef01234567",
"profileId": "prf_0123456789abcdef01234567",
"summary": "Concise report summary...",
"status": "published",
"generatedAt": "2026-07-21T11:59:48.000Z",
"language": "en",
"intelligence": {
"signalCount": 4,
"graphNodeCount": 12
},
"audio": {
"status": "available",
"durationSeconds": 318
}
},
"subscription": {
"id": "sub_0123456789abcdef01234567"
},
"links": {
"report": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567",
"markdown": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?format=markdown",
"sources": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?include=sources",
"signals": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/signals",
"graph": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/graph",
"audio": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/audio"
}
}
}
```
`summary` can be `null`, and audio can be marked `unavailable`. Following a link can require an
appropriately scoped API key or the endpoint's documented x402 flow.
## Request headers
"',
},
}}
/>
## Verify the signature
Compute HMAC-SHA256 over the exact raw request body bytes using the configured secret. Prefix the
hex digest with `sha256=` and compare it with `X-Y2-Signature` in constant time.
```javascript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyY2Webhook(rawBody, headers, secret) {
const supplied = headers["x-y2-signature"] ?? "";
const timestamp = Number(headers["x-y2-timestamp"] ?? 0);
const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - timestamp);
if (!timestamp || ageSeconds > 300) return false;
const expected = `sha256=${createHmac("sha256", secret)
.update(rawBody)
.digest("hex")}`;
const suppliedBytes = Buffer.from(supplied);
const expectedBytes = Buffer.from(expected);
return (
suppliedBytes.length === expectedBytes.length &&
timingSafeEqual(suppliedBytes, expectedBytes)
);
}
```
Use the raw bytes before JSON parsing; re-serializing an object can change the signed body. Keep
your own replay window policy and deduplicate with `Idempotency-Key` or `X-Y2-Event-Id`.
A secret is optional in the configuration form. If you omit it, Y2 does not send
`X-Y2-Signature`; secure the endpoint with a custom authorization header or another control.
## Acknowledge deliveries safely
The production delivery request has a 10-second timeout. Return a `2xx` response only after the
event is durably accepted, then process long-running work asynchronously.
```javascript
app.post("/y2/webhook", rawBodyMiddleware, async (request, response) => {
if (!verifyY2Webhook(request.rawBody, request.headers, process.env.Y2_WEBHOOK_SECRET)) {
return response.sendStatus(401);
}
await queue.put({
id: request.headers["x-y2-event-id"],
event: JSON.parse(request.rawBody.toString("utf8")),
});
return response.sendStatus(202);
});
```
Temporary failures (network errors, timeouts, HTTP 408/429, and 5xx responses) are retried up to
five total attempts per event, with delays of 5 seconds, 30 seconds, 5 minutes, and 30 minutes.
Other HTTP rejections pause the destination immediately. Five consecutive failures also pause
it; a successful delivery resets the failure count. Retrying delivery does not regenerate reports.
The configuration owner receives one warning email when temporary failures begin and one email
when delivery is paused. Reports, configuration, and delivery history are retained. Other delivery
destinations continue independently.
To recover a paused destination, save any endpoint, secret, or header changes, run **Test Webhook**,
then enable delivery within 15 minutes. Tests work while paused; changing the configuration or a
new production failure invalidates the test. Use **Delivery history and replay** to resend a
retained failed event after enabling. Replay preserves its original payload and event ID so the
receiver can deduplicate it. Enabling does not automatically send the backlog.
## Manage configurations
- Editing a masked webhook leaves its existing secret unchanged when the secret field is blank.
- Activating a paused webhook requires a current successful test and resets its failure count.
- An inactive webhook cannot be assigned for delivery.
- A configuration cannot be deleted while any subscription references it. Reassign those profiles
first.
- The app exposes 30-day success/failure statistics, last-use time, subscription count, and current
consecutive failures.
You can also manage configurations through the public API. API-key requests use the
`webhooks:manage` scope; creating new API keys requires Pro or Elite, while webhook delivery itself
is available on Lite.
## Next steps
Review management endpoints and integration boundaries.
Inspect canonical request and response schemas.
Retrieve report content linked by an event.
---
# Getting Started (/docs)
Set up Y2, choose what you want to monitor, and confirm where your intelligence reports will
arrive.
In about 10 minutes, you will have an active workspace, at least one intelligence profile, and
a delivery path you can verify in **My Profiles**.
## How Y2 works
An intelligence profile defines the subject Y2 monitors. Y2 runs research for that profile and
makes each completed report available in the app and through the delivery methods included with
your workspace plan.
## Before you begin
You need an email address or phone number that can receive a verification code. No API key is
required for this tutorial.
For Mac and Windows, see [desktop download availability](/download) and the
[installation guide](/docs/apps). Y2 Desktop is in beta; check the download page for published installers.
A Free workspace can subscribe to one community profile. Custom-profile creation and delivery
start with Lite. Pro adds scheduled-report management, SMS, audio, and new API keys. See
[Plans and limits](/docs/plans) for the complete reference.
## Set up your first intelligence workflow
### Sign in and finish onboarding
Open [Y2 sign in](/login), choose email or phone verification, and enter the code you receive.
Complete the workspace prompts before continuing.
Your setup is ready when Y2 opens your workspace dashboard.
### Choose how to start
Choose an existing briefing when you want a ready-made topic. This is the fastest path
and is available on Free.
Define your own topic, keywords, cadence, and delivery settings. Free includes one trial
profile; ongoing custom-profile delivery starts with Lite.
### Configure the profile
For a community profile, review its topic and cadence, then confirm the subscription.
For a custom profile, enter a clear name and topic, add useful keywords, and choose a
frequency. Start with a narrow subject you can recognize in the resulting report. The
[profile creation guide](/docs/profiles/creating) explains every field.
### Choose an available delivery method
Email is the default. Webhook delivery requires Lite or higher; SMS requires Pro or Elite.
You can change delivery settings later from the profile.
Receive the formatted report at the verified email address.
Send report JSON to an HTTPS endpoint on Lite, Pro, or Elite.
Receive a concise mobile summary on Pro or Elite.
### Confirm the setup
Open [My Profiles](/app/infoops/my-profiles). Your new profile or subscription should appear
in the list with its current status and delivery method.
The setup is complete when you can open the profile from this page. Custom reports follow the
configured cadence; completed reports remain available from the profile.
## Choose your next path
Learn how topics, schedules, sharing, branding, and report delivery fit together.
Use the Situation Room, source health, country cards, and investigation surfaces.
Authenticate, test requests in the Workbench, and use the generated endpoint reference.
Give an agent the contract, documentation bundle, and repository-specific integration prompt.
## Understand the platform
- [Mission](/docs/mission) explains why Y2 makes intelligence workflows broadly accessible.
- [Vision](/docs/vision) describes the direction of the platform.
- [Plans and limits](/docs/plans) lists current workspace capabilities and quotas.
- For help with your account, email [support@y2.dev](mailto:support@y2.dev).
---
# LLM-friendly Documentation (/docs/llms)
Y2 publishes build-generated text and MDX artifacts so an AI integration can discover, retrieve,
and cite the same documentation shown in Fumadocs.
Start with `/llms.txt`. Fetch one page when the task is narrow, use `/llms-full.txt` only when
broad product context is necessary, and use `/api/openapi.yaml` for request and response schemas.
## How the artifacts are produced
```mermaid
flowchart LR
source["content/docs MDX"] --> index["/llms.txt"]
source --> full["/llms-full.txt"]
source --> page["/docs/{slug}.mdx"]
source --> search["Fumadocs search index"]
openapi["public/api/openapi.yaml"] --> reference["Generated endpoint reference"]
```
The Vite documentation plugin reads `content/docs` during a production build. It emits the index,
full corpus, page-level MDX files, and the Fumadocs search index. During local development, the
same plugin generates these responses on request. The OpenAPI reference is generated separately
from `public/api/openapi.yaml`.
## Artifact reference
| Artifact | Content | Use it for |
|---|---|---|
| `/llms.txt` | Plain-text overview and grouped links to every documentation page | Discovery and routing |
| `/llms-full.txt` | Concatenated page titles, canonical paths, and source MDX bodies | Broad grounding or corpus ingestion |
| `/docs.mdx` | Source MDX body for the documentation home page | Targeted retrieval |
| `/docs/{slug}.mdx` | Source MDX body for one documentation page | Task-specific context |
| `/api/openapi.yaml` | OpenAPI contract for public HTTP endpoints | Types, parameters, schemas, scopes, and responses |
These are public documentation artifacts and do not require a Y2 API key.
Page-level files and `/llms-full.txt` contain the MDX body without frontmatter. Fumadocs tags such
as `Callout`, `Cards`, and `APIPage` can remain in the text. Generated endpoint pages may point to
`public/api/openapi.yaml` instead of expanding every schema, so API clients must also read the
published OpenAPI contract.
## Retrieve documentation
```bash
curl -fsS https://y2.dev/llms.txt
```
Use the grouped links and descriptions to decide which page the task needs.
```bash
curl -fsS https://y2.dev/docs/api/authentication.mdx
```
Append `.mdx` to a canonical documentation path. For example, `/docs/api` becomes
`/docs/api.mdx` and `/docs/profiles/creating` becomes `/docs/profiles/creating.mdx`.
```bash
curl -fsS https://y2.dev/llms-full.txt
```
Use this response when the agent needs relationships across several documentation sections.
```bash
curl -fsS https://y2.dev/api/openapi.yaml
```
Treat the operation definitions and schemas here as authoritative for HTTP integration code.
The deployed files use revalidation caching. Re-fetch them when starting a new integration task or
after a Y2 release instead of treating a local copy as permanent.
## Ingest the full corpus safely
Each page in `/llms-full.txt` begins with a generated header:
```text
# Page Title (/docs/canonical-path)
[Source MDX body]
```
The plugin places `---` between pages, but source pages can also contain their own horizontal
rules. Do not split on every `---`. Detect generated page headers that match a title followed by a
`/docs` path, and retain that path as source metadata.
### Fetch and validate the response
Require a successful HTTP response and a text content type before replacing an existing corpus.
### Detect page boundaries
Start a new document at headers shaped like `# Title (/docs/path)`. Keep the title and path
separate from the MDX body.
### Chunk within each page
Split long pages by their Markdown headings while preserving the canonical page path on every
chunk. Keep code blocks intact.
### Index the API contract separately
Parse `/api/openapi.yaml` by operation and schema. Do not infer endpoint shapes from examples
or from an `APIPage` component tag in the MDX corpus.
## Use MCP when the agent needs tools
The LLM artifacts provide documentation context only. Use the Y2 MCP integration when an agent
also needs bounded report or News reads, OpenAPI operation lookup, or Agent Y2 access.
`@y2-intel/mcp` is a separate package whose implementation is not in this platform repository.
After installation, use the MCP client's `tools/list`, `resources/list`, and `prompts/list`
responses as the source of truth for that installed version.
The [MCP guide](/docs/api/mcp) covers client configuration, scoped credentials, capability
inspection, and troubleshooting. Keep `Y2_API_KEY` in the MCP process environment, never in a
prompt or tool argument.
## Prepare a coding agent
Use the maintained integration prompt when an agent will write Y2 API code. It directs the agent
to the documentation index, OpenAPI contract, current base URLs, authentication rules, and plan
boundaries.
Ground an agent, constrain its scope, and verify the generated integration.
Choose the correct server, validate the contract, and regenerate client types.
Configure and inspect the separately distributed MCP server.
Build server-side TypeScript, Python, or command-line integrations from the contract.
---
# Mission (/docs/mission)
Y2 exists to make useful intelligence accessible to people and organizations that do not have a
dedicated research team.
## The problem
Important signals are scattered across feeds, reports, markets, maps, and specialized data
providers. Following them manually creates three recurring problems:
- **Coverage gaps:** no single source contains the full picture.
- **Attention limits:** people cannot continuously monitor every relevant source.
- **Lost context:** one-off searches rarely preserve what changed or why it matters.
Traditional intelligence services can address these problems, but their cost and operating model
often put them out of reach for individuals and smaller teams.
## Y2's approach
Profiles turn a recurring question into an intelligence workflow with a defined topic,
research cadence, and delivery path.
Live feeds, reports, entities, incidents, and relationships remain connected so readers can
move from an alert to the supporting context.
Intelligence can remain in the workspace or move through email, SMS, webhooks, audio, and the
API when the active plan supports them.
Global and community profiles let one well-designed monitoring workflow benefit more than one
person.
## Operating principles
### User-defined priorities
The operator decides what to monitor, which sources and workflows are relevant, and where results
should go. Automation serves that intent rather than replacing it.
### Traceable evidence
Reports and intelligence objects should retain enough source and provenance context for readers to
inspect the underlying evidence and form their own judgment.
### Human judgment remains final
Y2 can collect, organize, and synthesize information. It does not remove uncertainty or make a
decision on the operator's behalf.
### Broad access
The platform should support an individual testing one briefing, an analyst building a repeatable
workflow, and a team operating shared intelligence resources.
Open-source data can be incomplete, delayed, duplicated, or wrong. Treat generated analysis as
decision support, review cited evidence, and apply appropriate professional judgment.
## Who Y2 serves
Y2 is designed for analysts, investors, security professionals, researchers, operators, and
business leaders who need repeatable awareness of complex or fast-changing topics.
## Put the mission into practice
Set up a workspace and verify your first intelligence profile.
Explore live events, sources, country context, and connected intelligence.
Bring Y2 intelligence into an application or operational workflow.
---
# News Signal Feed (/docs/news)
The News Signal Feed lets signed-in users monitor cached RSS and X signals across 40 topics.
Select the topics relevant to your work, then switch between individual signals and AI-generated
12-hour recaps.
“Live Feed” describes the newest signals available in Y2's cache. RSS sources and recaps are
refreshed on scheduled cycles; X source ingestion currently runs on a separate weekly cycle.
## Monitor selected topics
### Open News
Sign in to Y2 and select **News** in the app navigation.
### Select feeds
Choose one or more chips under **Global Signal Feeds**. The current catalog contains 40
feeds organized into six groups:
| Group | Examples |
| --- | --- |
| Politics & Geopolitics | Geopolitics, US Politics, Defense |
| Macro & Markets | Macro, Equities, Rates & FX |
| Industry Verticals | Energy, Cyber, Biotech & Health |
| Regional Desks | MENA, Europe, Asia-Pacific |
| AI & Technology | AI, AI Agents, Machine Learning |
| Crypto & Digital Assets | Bitcoin, Ethereum, DeFi |
See [Topics and feeds](/docs/news/topics) for the complete catalog.
### Choose a view
Select **Live Feed** for individual signals or **12h Recap** for a per-topic synthesis.
### Open a source
When a signal has a source URL, select the row or **View Source** to open the original item
in a new tab.
Your selected feeds and view mode are saved to your account. Feed changes are written after a
short debounce, so the same selection can be restored on another signed-in device.
## Read a signal
Each Live Feed row can show:
Sentiment is a directional signal, not a confidence score or investment recommendation. Items
without a supported classification appear without a sentiment badge.
Treat generated sentiment and recaps as triage aids. Open the attributed source and verify
material claims before acting on them.
## Use 12-hour recaps
Select **12h Recap** to read the current cached recap for each selected feed. A recap card shows
the topic, creation time when available, and generated recap content.
The app currently exposes only the 12-hour view. The backend and public API accept additional
timeframe identifiers, but they are not selectable in the News interface and may not have cached
content for every topic.
See [AI news recaps](/docs/news/recaps) for interpretation and availability details.
## Refresh and stale data
The **Refresh** button re-runs the active Live Feed or Recap query. It does not start a new RSS or
X ingestion job. If the upstream cache has not changed, the same results remain visible.
Y2 displays a stale-data notice when the current query result is old. If no results appear:
1. Confirm that at least one feed is selected.
2. Try a broader set of feeds.
3. Select **Refresh** to re-read the cache.
4. If the cache is initializing, wait for the scheduled sync and try again.
## News feeds and country cards
The topic feed and the Situation Room's country cards answer different questions. News shows
cached items for your selected topics. Eligible items from ontology-enabled feeds can also become
observations linked to canonical entities and places.
Country-card news selects observations attributed to the chosen country, along with other
supported news and report sources. A topic item can therefore appear in News without appearing
on a country card: it may lack a supported country attribution, have expired, or not qualify for
the observation feed. Regional relevance and a country mentioned in a summary do not by themselves
establish the event's location.
For Red Sea coverage, explore MENA, Geopolitics, Logistics, and Energy topics as well as the
relevant [country cards](/docs/osint/country-cards). Opening a card reads stored observations;
it does not start a fresh news search.
## API access
The public API exposes cached signals, recaps, and the feed catalog:
Filter signals by topic and paginate results.
Retrieve cached topic recaps.
Discover available topic identifiers.
API-key requests require the `news:read` scope. New API keys require Pro or Elite; eligible
existing Lite keys retain their historical grace limits. These endpoints also support the x402
flow described in their generated references.
## Next steps
Review the complete feed catalog and topic identifiers.
Learn what the 12-hour synthesis contains and how to use it.
Configure an API key and scopes.
---
# AI News Recaps (/docs/news/recaps)
AI News Recaps condense recent signals for each selected topic into a cached narrative. Use them
to establish context quickly, then open the underlying Live Feed sources before making a
decision.
```mermaid
flowchart LR
A[Topic cache] --> B[12h window]
B --> C[Up to 24 recent signals]
C --> D{LLM recap available?}
D -->|Yes| E[Structured recap]
D -->|No or failed| F[Deterministic fallback]
E --> G[Recap cache]
F --> G
G --> H[News interface]
G --> I[Public API]
```
## Read recaps in the app
### Open News
Select **News** in the signed-in app.
### Select topics
Choose one or more feeds under **Global Signal Feeds**.
### Switch to 12h Recap
Select **12h Recap** in the page header. Each selected topic with cached content receives a
separate card.
### Verify important signals
Return to **Live Feed** and open attributed sources for any claim that affects your work.
The dashboard currently exposes only the 12-hour recap view. All 40 topics in the current feed
registry are configured for that window.
## Generation schedule
RSS ingestion starts at 00:00, 06:00, 12:00, and 18:00 UTC. Recap scheduling begins two minutes
after each of those cycles, then staggers topic jobs by 45 seconds to avoid starting all 40 at
once.
Recaps are pre-generated and cached. Opening the page or selecting **Refresh** re-reads the
cache; it does not generate a new recap on demand.
X ingestion currently runs on a separate weekly schedule. A recap can therefore combine recently
cached RSS signals with X signals captured during the latest X cycle.
## Recap contents
When structured generation succeeds, the cached record can contain:
The app prioritizes the `recap` narrative. The API returns the complete cached object for each
topic, so integrations should tolerate additional fields and the fallback shape.
### Fallback output
If structured LLM generation is unavailable or fails, Y2 creates a deterministic recap from the
recent cached signals. The fallback can include the narrative, item and source counts, source
mix, and top signals. This keeps the topic cache usable without presenting failed model output as
a successful structured result.
## Retrieve recaps with the API
```bash
curl "https://api.y2.dev/api/v1/news/recaps?topics=bitcoin,ethereum&timeframe=12h" \
-H "Authorization: Bearer y2_your_api_key"
```
The response maps each available topic ID to its cached recap:
```json
{
"data": {
"bitcoin": {
"recap": "Bitcoin 12-hour recap content...",
"executive_summary": "The highest-signal developments...",
"sentiment": "neutral",
"item_count": 12,
"created_at": "2026-07-21T12:18:00.000Z"
}
},
"meta": {
"timeframe": "12h",
"topics": ["bitcoin", "ethereum"]
}
}
```
The API schema accepts `12h`, `24h`, `3d`, and `7d`, but the current registry schedules only
`12h` cache entries. A valid longer timeframe can therefore return no recap for a topic.
See the generated [List recaps reference](/docs/api/reference/news/listRecaps) for the canonical
authentication, query, and response schema.
## Interpret recaps safely
A recap is generated from a bounded set of cached signals. It may omit events, repeat source
errors, or infer a misleading emphasis. It is not a substitute for source review or
professional financial, legal, security, or policy advice.
- A missing topic card can mean that no successful recap is cached for that selection.
- Quiet topics can produce a neutral “no direct signals” message.
- `item_count` describes the items considered by that recap, not total market activity.
- Sentiment summarizes the supplied signals; it does not forecast price or outcome.
- Source IDs in structured fields identify inputs, but you should use Live Feed source links for
direct verification.
## Next steps
Inspect individual signals and open their sources.
Choose feeds for the app or API.
Review authentication and the canonical response schema.
---
# Topics and Feeds (/docs/news/topics)
Y2 currently defines 40 News feeds in a shared source registry. The app and API use the IDs on
this page to select topic caches.
The feed catalog is configuration-driven. Use `GET /api/v1/news/feeds` when an integration
needs to discover the current list at runtime instead of storing a copy.
## Feed catalog
| Feed | ID | Coverage |
| --- | --- | --- |
| Geopolitics | `geopolitics` | Alliances, wars, sanctions, and diplomatic flashpoints |
| US Politics | `politics_us` | Federal politics, elections, Congress, and major policy shifts |
| Defense | `defense` | Military operations, NATO, weapons programs, and security policy |
| Sanctions & Trade | `sanctions_trade` | Export controls, tariffs, sanctions, and industrial policy |
| Feed | ID | Coverage |
| --- | --- | --- |
| Macro | `macro` | High-impact macroeconomic and geopolitical events with market relevance |
| Equities | `equities` | Global equity markets, earnings, indices, and market structure |
| Rates & FX | `rates_fx` | Interest rates, currencies, central banks, and yield curves |
| Commodities | `commodities` | Energy, metals, agriculture, and commodity-market shocks |
| Banking & Regulation | `banking_reg` | Banking stress, financial regulation, and financial stability |
| Feed | ID | Coverage |
| --- | --- | --- |
| Energy | `energy` | Oil and gas, power markets, grids, nuclear, and renewables |
| Cyber | `cyber` | Attacks, ransomware, vulnerabilities, infrastructure, and threat actors |
| Semiconductors | `semiconductors` | Chip manufacturing, foundries, accelerators, and supply chains |
| Biotech & Health | `biotech` | Pharma, biotech, health alerts, and life-science market events |
| Logistics & Maritime | `logistics` | Ports, shipping, chokepoints, supply chains, and disruptions |
| Critical Minerals | `critical_minerals` | Rare earths, lithium, copper, mining, and battery materials |
| Telecom | `telecom` | 5G, submarine cables, satellite broadband, and network infrastructure |
| Feed | ID | Coverage |
| --- | --- | --- |
| MENA | `region_mena` | Middle East and North Africa political, conflict, and economic signals |
| Europe | `region_europe` | European politics, EU policy, security, and regional economics |
| Asia-Pacific | `region_asiapac` | China, Indo-Pacific security, ASEAN, and regional political economy |
| Latin America | `region_latam` | Politics, security, crime, and economic signals in Latin America |
| Africa | `region_africa` | Politics, conflict, resources, and regional economic developments |
| North America | `region_namerica` | US, Canada, and Mexico cross-border political and economic signals |
| Feed | ID | Coverage |
| --- | --- | --- |
| AI | `ai` | LLMs, infrastructure, model launches, and major partnerships |
| AI Agents | `ai_agents` | Agent frameworks, incentives, ecosystems, and launches |
| Data | `dats` | Data, analytics, and infrastructure developments |
| Machine Learning | `machine_learning` | Academic, industry, and open-source ML developments |
| Tech | `tech` | Software, semiconductors, regulation, and AI infrastructure |
| Feed | ID | Coverage |
| --- | --- | --- |
| Base | `base` | Base L2 network and ecosystem developments |
| Bitcoin | `bitcoin` | Network upgrades, ETF flows, miners, and adoption |
| Crypto | `crypto` | Broad markets, launches, infrastructure, and protocols |
| DeFi | `defi` | Protocols, yield markets, governance, and infrastructure |
| Ethereum | `ethereum` | Protocol upgrades, scaling, validators, and ecosystem changes |
| Hyperliquid | `hyperliquid` | HYPE, governance, perps liquidity, and ecosystem projects |
| On-Chain Whales | `on_chain_whale` | Large transactions, wallets, and whale activity |
| Perps | `perps` | Perpetual futures, derivatives, leverage, and trading platforms |
| Ripple | `ripple` | XRP, XRPL, Ripple Labs, and related legal developments |
| RWA | `rwa` | Tokenized real-world assets, issuers, platforms, and regulation |
| Solana | `solana` | Protocol, validator, outage, and core-network developments |
| Token Listings | `token_listings` | Exchange additions, token listings, and launches |
| Virtuals | `virtuals` | Virtuals Protocol, GAME, agents, commerce, and tokenomics |
Every topic in the current registry uses a 12-hour recap window.
## Select feeds in the app
### Open News
Go to **News** and find **Global Signal Feeds**.
### Toggle topic chips
Select a chip to add that feed. Select it again to remove it. You can combine topics from
any group.
### Allow the preference to save
Feed changes are saved after a one-second debounce. The app restores the saved selection on
later visits.
The dashboard does not apply the API's default topic list to your saved preferences. If no feeds
are selected, choose at least one chip before the Live Feed or Recap view can load data.
## Choose a useful combination
Start narrow, then add broader feeds only when they contribute different coverage.
`macro`, `equities`, `rates_fx`, `geopolitics`
`ai`, `semiconductors`, `critical_minerals`, `energy`
`cyber`, `defense`, `telecom`, `geopolitics`
`crypto`, `bitcoin`, `ethereum`, `defi`
Broad and narrow topics can overlap. For example, `crypto` can include events also found under
`bitcoin` or `ethereum`. More feeds increase coverage and can also increase duplicate context.
## Use topic IDs with the API
Pass a comma-separated list to the `topics` query parameter:
```bash
curl "https://api.y2.dev/api/v1/news?topics=geopolitics,macro,energy&limit=50" \
-H "Authorization: Bearer y2_your_api_key"
```
If `topics` is omitted, the API currently uses:
```text
crypto, geopolitics, macro, equities, ai, energy
```
Unknown topic IDs return `400 Bad Request`.
### Discover feeds dynamically
```bash
curl "https://api.y2.dev/api/v1/news/feeds" \
-H "Authorization: Bearer y2_your_api_key"
```
Each feed entry includes its `id`, display names, description, group metadata, UI color, and
ontology-ingestion flag. Response metadata includes the catalog count and API default topics.
See the generated [List feeds reference](/docs/api/reference/news/listFeeds) for the canonical
response schema and authentication options.
## Next steps
Filter current signals and open attributed sources.
Understand recap availability and limitations.
Review pagination, filters, and response fields.
---
# Catalog and Ledger Alignment (/docs/ontology/explanation/catalog-alignment)
Y2 has two ontology layers with different jobs.
| | Shared catalog | Workspace ledger |
| --- | --- | --- |
| Holds | One entity per real-world thing, with names, aliases, external IDs, and relations | Evidenced claims about subjects your workspace researches |
| Built by | Ingestion pipelines, extraction, and enrichment across Y2 | Analysts and integrations in your workspace |
| Identity | Resolved: records that share a name or alias within a kind resolve to one entity | Never merged on a shared string; `same-as` stays a hypothesis |
| Changes | Updated in place; merges leave redirects | Append-only; every correction is a new line |
| Grades | A confidence on each relation | A verification state and confidence on each claim, tied to cited observations |
| Visibility | Shared across Y2 | Private to the workspace |
The catalog is a resolved projection that makes search, graphs, and fusion fast. The ledger is the
claim store that makes research accountable. Neither replaces the other.
## Anchors connect them
A ledger subject can be **anchored** to a catalog entity. The anchor is an index between the layers:
it is not a claim and not a merge. Two subjects may share one anchor, for example while a `same-as`
hypothesis is contested. When catalog entities merge, anchors follow the surviving entity.
Anchoring respects each layer's classes:
| Catalog kind | Ledger class |
| --- | --- |
| `person` | `person` |
| `organization`, `vendor` | `organization` (a vendor is an organization in a market role) |
| `threat_actor` | `organization` or `person`; the actor assessment stays in the catalog |
| `software`, `api_service`, `ai_model` | `system` |
| `cve`, `malware_family`, `indicator` | Not anchored: vulnerability and adversary-behavior content stays in the catalog |
| `country`, `region`, `facility`, `vessel`, `aircraft`, `asset`, `protocol` | Not anchored: places, tracked assets, and standards are not passive-research subjects |
## Relations map where the meaning matches
| Catalog relation | Ledger predicate | Used by fusion as |
| --- | --- | --- |
| `supplies` | `supplies` | Supplier or customer path |
| `subsidiary_of` | `subsidiary-of` | Parent or subsidiary path |
| `operates` | `operates` | Operated-system path |
| `part_of` | `affiliated-with` (the catalog tie carries no role) | Not a path |
| `affects`, `targets`, `uses` | None: vulnerability and adversary behavior | Cyber exposure events |
| `ally_of`, `adversary_of`, `located_in` | None: geopolitical assessments and geography | Not a path |
| `references` | None: a document reference | Not a path |
## Catalog identifiers are candidates, not claims
The catalog may already hold a company's Wikidata ID, ASN, website, and social profiles. Those are
useful starting points, but they arrive without the retrieval, excerpt, and verification state a
ledger claim needs, so Y2 never copies them into the ledger as claims. Record them yourself with the
page that supports them. The catalog's combined CIK-or-ticker field is never guessed into either
namespace: record a `sec-cik` or a `ticker` designator from the filing or exchange page.
## Why keep them separate
Resolving identity in the catalog is the right trade-off for a shared index of well-known
organizations, CVEs, and places: it keeps graphs connected and search fast. The same rule is the
wrong one for research about people and less-known parties, where two records that share a name are
often two different people. Keeping the ledger separate lets each layer use the identity rule that
fits its job, while anchors let fusion read both.
---
# Why a Claim Ledger (/docs/ontology/explanation/claim-ledger)
Most knowledge graphs store the current answer: one node per thing, properties overwritten as new
data arrives, and a merge whenever two records look alike. That shape is fast to query and easy to
get wrong in ways you cannot see later. Y2's entity ledger stores something different: what a public
source said, when it was retrieved, how strongly it supports a claim, and when that claim is taken to
hold. The picture you read is computed from those claims.
## The subject of a claim is not its string
A person, organization, or system is a **subject**: a row opened with a class and nothing else.
Everything that can change arrives as a **claim** about it. A name, a handle, a domain, or a CIK is a
**designator**: one public string, stored once.
Keeping the string and the subject apart is what prevents the most common identity error in
collected intelligence. Two people who share a published name, or two companies whose aliases
overlap, are two subjects that happen to be designated by one string. Graph platforms that derive an
entity's identity from its name and aliases collapse them. In the ledger, a shared designator is only
a shared string. Subjects merge only by a human decision, made by reading a `same-as` hypothesis that
the ledger still holds as a hypothesis; when a `distinct-from` is also live, the pair is contested
and both rows remain.
## Every change is a new line
Claims are appended, never edited. A correction supersedes the current head of its claim key; a
retraction clears the grade and interval but still cites the observation that says why. Each line is
sealed to the one before it with SHA-256, so an edited history fails verification.
This costs some storage and buys three things:
- **Accountability.** You can show what you believed, on what evidence, before a correction.
- **Safe collaboration.** A line must name the head it replaces, so two analysts working from stale
views cannot silently overwrite each other; the second write is rejected.
- **Portability.** The canonical log is a plain NDJSON file that anyone can check with the reference
checker, independent of Y2.
## Verification belongs to the claim
A person is not "verified." A specific handle claim is `located` because one page ties it, or
`cross-linked` because two different pages identify it. The ledger enforces that the state matches
the evidence: `cross-linked` needs two retrieved pages; `analytic` means the only support is an
analyst's note, which is how hypotheses and display labels are graded. This follows the Berkeley
Protocol's separation of provenance, source evaluation, and verification, and keeps the grade on the
thing that was actually checked.
## Three clocks
| Clock | Field | Question |
| --- | --- | --- |
| Record time | `recorded_at` | What had the ledger written by then? |
| Valid time | `valid_from`, `valid_to` | When is the claim taken to hold in the world? |
| Retrieval time | `retrieved_at` | When was the page fetched? |
Record time selects a prefix of the log: what the research believed at that moment. Valid time filters
the heads of that prefix. Asking "what did we believe on March 20 about April 10" sets both. A missing
bound means unknown, never a guessed date: a page that says "since 2023" is quoted in the excerpt
until a source gives a day. When the only evidence is that a page showed something between two
retrievals, the interval is a sighting interval and the note says so.
## Passive by construction
The method vocabulary is closed and public: web pages, profiles, DNS, certificate transparency,
archives, registries, code hosts, and analyst notes that cite them. Interaction with the subject,
authentication, and non-public access have no name in the vocabulary, so they cannot be recorded as
if they were valid sources. The ledger keeps a short excerpt and its hash, not page bodies, and Y2
never writes a personal name into a sealed line.
## Where the design comes from
The ledger borrows its publishing pattern from MITRE ATT&CK (stable IDs, every statement sourced),
its interchange from STIX 2.1 (identities, observables, relationships with confidence and time
bounds), its provenance fields from W3C PROV-O, and its method duties from the Berkeley Protocol. It
deliberately differs from graph platforms that upsert on a deterministic ID and deduplicate nearby
edges: the ledger keeps every correction. See [STIX 2.1 export](/docs/ontology/reference/stix-export)
for the projection, and [Catalog and ledger alignment](/docs/ontology/explanation/catalog-alignment)
for how the ledger relates to Y2's shared catalog.
---
# Cyber-Market Fusion (/docs/ontology/explanation/cyber-market-fusion)
Supply chains, geopolitics, cyber events, and market economics are increasingly one system. A
ransomware incident at a logistics provider becomes a missed quarter for the retailer that depends on
it. A sanctions action reroutes shipping and moves a commodity. A critical vulnerability in a widely
deployed product becomes a disclosure event for every issuer that runs it. Organizations that see
these connections early act before their competitors do; most see them late, because the evidence
sits in different teams, tools, and feeds.
Y2 treats open-source information as a first-class intelligence source and organizes it with an
ontology: a semantic layer that says what each record is about and how the things it mentions relate.
Cyber-market fusion is where that layer pays off for one question: **what is happening to this company
and to the parties it depends on?**
## What the fused view does
For an organization or vendor, fusion:
1. **Builds the exposure path.** Suppliers, customers, parents, subsidiaries, and operated systems come
from the shared catalog's relations and from your workspace's evidenced ledger ties. A ledger tie
carries its source and verification state, so you can see why a party is on the path.
2. **Gathers events about everyone on the path.** CVEs that affect them, actors that target them, Y2
signals by domain, incidents, prediction markets, and your workspace's financial facts.
3. **Places them on one timeline**, labeled by domain, severity, path, and provenance, with every event
linked to its source.
4. **Pairs cyber events with the market events that followed them** on the timeline, within a window
you choose.
5. **Shows which of your topics sit on the path**, so a supplier's breach surfaces on the topic that
tracks the company that depends on it.
## What it deliberately does not claim
An adjacency says that a market event followed a cyber event within the window, and whether both
reached the company along the same path. It does not say that one caused the other. Markets move for
many reasons, and a timeline cannot separate them. Fusion shows the timing so an analyst can
investigate; it does not produce a causal score, and it does not hide the uncertainty in a single
number.
The view is also honest about coverage. Events without a source time stay in an **Undated** list
instead of appearing at a guessed date. Every source is read within a budget, and the response says
which budgets were reached and which sources come from a bounded recent scan. An empty timeline means
no events were found within those limits; it does not mean the company has no exposure.
## Why the ledger matters here
A fused view is only as good as its exposure path. Catalog relations are broad but come without a
retrieval, an excerpt, or a verification state. Ledger ties are narrower and carry all three: the
supplier is on the path because a named public page says so, graded `located` or `cross-linked`.
Recording the ties that matter to your analysis, with evidence, is what makes the fused view yours
and defensible.
## Related
- [Tutorial: map a company's exposure](/docs/ontology/tutorial)
- [Fusion response](/docs/ontology/reference/fusion)
- [Why a claim ledger](/docs/ontology/explanation/claim-ledger)
---
# Correct, Supersede, or Retract a Claim (/docs/ontology/how-to/correct-a-claim)
The ledger never edits a line. Every change is a new line for the same claim key
(`predicate|from|to|qualifier`) that supersedes the key's current head. The earlier lines stay in the
history, so you can always see what was believed before.
## Find the current head
In the app, the ledger panel shows the live claim for each designation and tie. Through the API, fold
the subject and read `claimId` from the claim:
```bash
curl https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567 \
--header "Authorization: Bearer $Y2_API_KEY"
```
## Strengthen a claim with a second source
In the app, record the same identifier again with the second page and a higher state such as
`cross-linked`. The new line supersedes the head and keeps citing the head's evidence, so both pages
support it.
Through the API, send `supersedes` with the head's `claimId` and cite both observations:
```bash
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--json '{"kind":"claim","claim":{"predicate":"designated-by","from":"sbj_…","to":"dsg_…","supersedes":"clm_…head","confidence":90,"verification":"cross-linked","evidenceRefs":["obv_…first","obv_…second"]}}'
```
## Close an interval when a page changes
When a later retrieval no longer shows an identifier, supersede the claim with an end bound instead of
retracting it. The identifier held for the interval you observed.
```json
{
"kind": "claim",
"claim": {
"predicate": "designated-by",
"from": "sbj_…",
"to": "dsg_…",
"supersedes": "clm_…head",
"confidence": 75,
"verification": "self-asserted",
"validFrom": "2026-03-01T09:55:00Z",
"validTo": "2026-04-02T09:00:00Z",
"evidenceRefs": ["obv_…first-retrieval", "obv_…later-retrieval"],
"note": "Sighting interval between two retrievals; the page may have listed it earlier."
}
}
```
The end is exclusive. Leave a bound empty when no source gives it: an empty bound means unknown,
never a guessed date.
## Retract a mistaken claim
A retraction clears confidence, verification, and the interval, and still cites the observation that
explains why, usually an analyst note:
```json
{
"kind": "claim",
"claim": {
"op": "retract",
"predicate": "designated-by",
"from": "sbj_…",
"to": "dsg_…",
"supersedes": "clm_…head",
"evidenceRefs": ["obv_…note"],
"note": "Attached on a shared name token only."
}
}
```
The retracted head leaves the present picture and stays in history. You can assert the same key again
later by superseding the retraction.
## If the request is rejected
| Response | Cause | Fix |
| --- | --- | --- |
| `409` with `assertion.chain` | `supersedes` is missing, or points at a line that is no longer the head | Fold the subject again and supersede the current head |
| `400` with `assertion.verification` | The state is stronger than the cited evidence | Cite a second page, or lower the state |
| `400` with `assertion.retract` | A retraction carries confidence, verification, or an interval | Remove them |
See [Ledger record format](/docs/ontology/reference/ledger-format#rules) for every rule code.
---
# Export STIX and Verify the Seal (/docs/ontology/how-to/export-and-verify)
## Export a subject as STIX 2.1
In the app, choose **Export STIX 2.1** in the ledger panel. It downloads a bundle for the subject at
the **Valid on** date you set.
Through the API, with `ledger:read`:
```bash
curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567/stix" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "history=true" > northwind.stix.json
```
`history=true` adds retracted claims as relationships with `revoked: true`. Without it, the bundle
holds only live claims. See [STIX 2.1 export](/docs/ontology/reference/stix-export) for the mapping.
An incomplete subject neighborhood returns `400 VALIDATION_ERROR` instead of a partial bundle.
Export the complete sealed log below, then run `bun packages/ontology/src/cli.ts stix ledger.ndjson`
to project it offline.
## Export the sealed log
Page through the log with `afterSeq`. The log is append-only, so continuing from `meta.nextAfterSeq`
never skips or repeats a line:
```bash
after=0
: > ledger.ndjson
while :; do
page=$(curl --silent --get "https://api.y2.dev/api/v2/ledger/records" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "afterSeq=$after" --data-urlencode "limit=200")
echo "$page" | jq -r '.data[].line' >> ledger.ndjson
after=$(echo "$page" | jq '.meta.nextAfterSeq')
[ "$(echo "$page" | jq '.meta.hasMore')" = "true" ] || break
done
```
Each `line` is the exact canonical text that was sealed. Write it byte for byte; do not reformat it.
## Check the export offline
The reference checker ships in the portable `@y2/ontology` package. From the root of the Y2
repository:
```bash
bun packages/ontology/src/cli.ts check ledger.ndjson
```
It verifies canonical JSON, each line's SHA-256, the `prev` chain, strictly increasing record time,
and every rule in the [record format](/docs/ontology/reference/ledger-format). It prints `ok` or one
line per problem with its rule code. `fold` and `stix` subcommands read the same file with
`--recorded-at` and `--valid-at` cursors.
## Verify the seal in place
To check the stored log without exporting it, verify windows of the seal:
```bash
curl --get "https://api.y2.dev/api/v2/ledger/verify" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "afterSeq=0" --data-urlencode "limit=200"
```
The response reports `ok`, the number of lines checked, and any problems by `seq`. Continue from
`meta.nextAfterSeq` until `hasMore` is false. Seal verification recomputes hashes and links; rules that
look up earlier records are enforced at append time and by the offline checker.
---
# Read the Ledger at a Point in Time (/docs/ontology/how-to/read-at-a-point-in-time)
Two cursors control a fold. Set either, both, or neither:
| Cursor | Parameter | Question it answers |
| --- | --- | --- |
| Record time | `recordedAt` | What had the ledger recorded by then? |
| Valid time | `validAt` | Which claims hold in the world at that time? |
## See what holds on a date
In the app, set **Valid on** in the ledger panel. Designations whose interval excludes that date move
to **Outside the valid interval**; they are not deleted.
Through the API:
```bash
curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "validAt=2026-05-01T00:00:00Z"
```
## See what you believed before a correction
Set `recordedAt` to a time before the correction line was recorded. The fold uses only lines recorded
by then, so a claim you later retracted is still the head:
```bash
curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "recordedAt=2026-03-05T12:00:05Z"
```
## Combine both
"What did we believe on March 20 about April 10?" sets both:
```bash
curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "recordedAt=2026-03-20T00:00:00Z" \
--data-urlencode "validAt=2026-04-10T00:00:00Z"
```
Record time selects a prefix of the log; valid time then filters the heads of that prefix. Valid time
never walks back to an older version of a claim.
## Export the same view
The STIX export takes the same cursors, so a partner receives exactly the picture you read:
```bash
curl --get "https://api.y2.dev/api/v2/ledger/subjects/sbj_0123456789abcdef01234567/stix" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "recordedAt=2026-03-20T00:00:00Z" \
--data-urlencode "validAt=2026-04-10T00:00:00Z"
```
Times accept any ISO 8601 instant; the ledger normalizes them to UTC milliseconds.
---
# Record a Claim with Evidence (/docs/ontology/how-to/record-evidence)
Use this when a public page tells you something about a company, person, or system: a domain it
uses, its SEC CIK, the host that serves its portal, or a supplier it depends on.
You need an owner, admin, or member role in the workspace. In the API, use a workspace-bound key with
`ledger:write`.
## Record an identifier in the app
1. Open the company from **FININT → Markets → Companies**. If **Entity ledger** has no subject,
choose **Open ledger subject**.
2. In **Record an identifier with its source**, choose the kind and enter the value as the page shows
it. Y2 stores the canonical form: lowercase email and hosts, `AS` plus the number for an ASN, a CIK
padded to ten digits, and an uppercase ticker.
3. Set the namespace when the kind needs one:
| Kind | Namespace |
| --- | --- |
| `handle` | The platform or host, such as `github.com` |
| `external-id` | `sec-cik`, `lei`, `wikidata`, or `attack-group` |
| `ticker` | The exchange's ISO 10383 MIC, such as `XNAS` |
| `public-key` | The algorithm, such as `ssh-ed25519` |
4. Enter the public source URL, the method, and a short excerpt (the quote that shows the
identifier). The excerpt is capped at 500 characters; the ledger keeps a quote and its SHA-256,
never a page body.
5. Choose the verification state the evidence supports, and a confidence from 0 to 100.
6. Choose **Record claim**.
## Record a supply or ownership tie in the app
In **Record a supply or ownership tie**, search the shared catalog for the other company, pick its
role (supplier, customer, parent, or subsidiary), cite the page that states the tie, and choose
**Record tie**. Y2 opens a ledger subject for the other company if your workspace has none and anchors
it to the catalog entity.
## Record through the API
Append the observation first, then the designator, then the claim that cites the observation:
```bash
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Idempotency-Key: dns-portal-2026-06-01" \
--json '{"kind":"observation","observation":{"method":"public-dns","sourceGrade":"primary","sourceUrl":"https://dns.example/lookup?name=portal.northwind.example","retrievedAt":"2026-06-01T11:50:00Z","excerpt":"portal.northwind.example A 192.0.2.10 (AS64500)","collector":"dns-sync"}}'
```
A designator with the same kind, namespace, and normalized value is reused: the response is `200`
with `created: false` and the existing `dsg_…` ID. A claim that repeats a claim key must supersede the
current head (see [Correct, supersede, or retract a claim](/docs/ontology/how-to/correct-a-claim)).
## Choose the verification state
Pick the lowest state that is true. The ledger rejects states the cited evidence cannot support.
| If the evidence is | Use |
| --- | --- |
| One public page that ties the identifier to the subject | `located` |
| The subject's own page stating it | `self-asserted` |
| Two retrieved pages at different URLs that each identify it | `cross-linked` |
| A further independent source that agrees | `corroborated` |
| Only an analyst note connecting other observations | `analytic` |
| A page where the string appears without a tie | `mentioned` |
`cross-linked` and `corroborated` need at least two retrievals of different pages among the cited
observations. `analytic` means every cited observation is an analyst note.
A public profile shows that a handle exists and what it displays. It does not show who can sign in
to it. Record `designated-by` for the handle, and record `controls` only when a public source speaks
to control, usually at low confidence.
## Related
- [Vocabulary](/docs/ontology/reference/vocabulary) for every kind, method, and predicate.
- [Ledger record format](/docs/ontology/reference/ledger-format) for the rules and their error codes.
---
# Ontology and Cyber-Market Fusion (/docs/ontology)
Most enterprise data loses its meaning at department boundaries: the security team tracks a vendor's
hosts, the finance team tracks its filings, and nobody connects a breach at a supplier to a guidance
cut two days later. Y2 keeps those threads together in two layers:
- **The entity ledger** is your workspace's append-only record of what public sources say about a
company, person, or system: its names, domains, ASNs, handles, SEC CIK, ticker, suppliers, and the
systems it operates. Every claim cites a public observation and carries a verification state and a
time interval. Nothing is edited in place and nothing is merged on a shared name.
- **Cyber-market fusion** reads the ledger together with Y2's shared catalog to place cyber, market,
supply-chain, and geopolitical events about a company, and about the parties it depends on, on one
timeline. It shows which of your topics sit on that exposure path.
```mermaid
flowchart LR
subgraph workspace [Your workspace]
L[Entity ledger: evidenced claims]
F[Financial history]
T[Topics]
end
subgraph catalog [Y2 shared catalog]
E[Entities and relations]
C[CVEs and threat actors]
S[Signals, incidents, markets]
end
L -- anchored to --> E
E --> X[Cyber-market fusion]
L --> X
C --> X
S --> X
F --> X
X --> T
```
## Find what you need
This section follows the [Diátaxis](https://diataxis.fr/) structure: tutorials to learn, how-to
guides to get a task done, reference to look things up, and explanation to understand the design.
Anchor a ledger subject, record evidence, and read the fused timeline, start to finish
### How-to guides
Add identifiers, systems, and supply ties that cite a public page
Change what the ledger believes without rewriting history
Ask what was recorded by one date about the world on another
Share claims as STIX 2.1 and prove the log was not edited
### Reference
Records, fields, the seal, IDs, and every rule a line must satisfy
Subject classes, designator kinds, methods, predicates, and the verification ladder
How ledger records map to STIX objects and the Y2 extension
Domains, exposure paths, provenance, adjacencies, and coverage
HTTP endpoints, scopes, and the generated operation reference
### Explanation
Append-only claims, three clocks, and identity that never merges on a shared string
The thesis, what fusion shows, and what it deliberately does not claim
How the shared catalog and your workspace ledger fit together
---
# Fusion Response (/docs/ontology/reference/fusion)
`GET /api/v2/entities/{entityId}/fusion` and the in-app **Cyber-market exposure** panel return the
same view for an organization or vendor entity.
## Parameters
| Parameter | Default | Range | Meaning |
| --- | --- | --- | --- |
| `windowDays` | 90 | 1–365 | Days of history ending at request time |
| `adjacencyDays` | 7 | 1–90 | How long after a cyber event a market event counts as adjacent |
## Response fields
| Field | Meaning |
| --- | --- |
| `entity` | The resolved company (after catalog merge redirects) |
| `window` | `start`, `end`, and `adjacencyDays` actually used |
| `counterparties` | Up to 12 parties on the exposure path, with `relation`, `provenance`, and, for ledger ties, `confidence` and `verification` |
| `identifiers` | Live designations of ledger subjects anchored to the company |
| `topics` | Workspace topics whose subject is the company or a counterparty, with the path relation |
| `timeline` | Up to 200 dated events in the window, newest first |
| `undated` | Events without a source time, never placed at a guessed date |
| `summaries` | Per domain: event count, highest severity, latest event time |
| `adjacencies` | Up to 50 cyber → market pairs within `adjacencyDays`, same-path pairs first |
| `coverage` | `truncated` and plain-language notes on read budgets and source limits |
## Exposure paths
| Relation | From a catalog relation | From a ledger claim |
| --- | --- | --- |
| `supplier` | The party `supplies` the company | The party's subject `supplies` the company's subject |
| `customer` | The company `supplies` the party | The company's subject `supplies` the party's subject |
| `parent` | The company is `subsidiary_of` the party | The company's subject is `subsidiary-of` the party's subject |
| `subsidiary` | The party is `subsidiary_of` the company | The party's subject is `subsidiary-of` the company's subject |
| `operated-system` | The company `operates` or `uses` a software, API service, or model entity | The company's subject `operates` a system subject |
A ledger tie wins over a catalog tie to the same entity because it carries evidence and a
verification state.
Catalog exposure paths use the resolved relation projection. Changing `windowDays` filters event
times; it does not reconstruct historical catalog relations or what Y2 knew at an earlier date.
Ledger exposure paths use claims valid at `window.end`.
## Event sources
| Domain | Sources |
| --- | --- |
| `cyber` | CVEs that `affect`, and actors that `target`, the company or a path party; readable cyber signals; cyber incidents |
| `market` | Readable market signals; the workspace's financial facts at their information date; prediction markets about a path party; economic incidents |
| `supply_chain` | Ledger supply and ownership ties at their `valid_from`; readable supply-chain signals; maritime and aviation incidents |
| `geopolitical` | Readable geopolitical, military, and policy signals; conflict and political incidents |
Signals are readable when they are global or community, belong to the key's workspace, or are the
caller's own private signals.
## Provenance
| Value | Meaning |
| --- | --- |
| `catalog` | Y2's shared catalog: entities, relations, signals, incidents, markets |
| `ledger` | Your workspace's entity ledger |
| `workspace` | Your workspace's research, such as company financial history |
## Read budgets
Fusion never scans without a bound. Catalog relations read up to 150 in each direction; each path
party reads its 10 most recently added `affects` and 10 `targets` relations, selecting CVEs, threat
actors, and malware for cyber events, and up to 15 signal subjects in the selected window. Financial
history contributes the 12 most recently recorded facts;
up to 5 anchored ledger subjects are folded and 200 workspace topics are read. Incidents and
prediction markets still store entity links as arrays, so they come from the 100 most recently
updated rows. `coverage.notes` states these limits in each response, and `coverage.truncated` is true
when any source or output budget omits rows. A truncated response can therefore contain fewer than
200 events. Sources naming both the company and a counterparty retain the direct exposure path.
---
# Ledger Record Format (/docs/ontology/reference/ledger-format)
A ledger is a sequence of lines. Each line is one record, serialized as canonical JSON: object keys
sorted by UTF-16 code unit, no insignificant whitespace, and integers only. Every field is present;
`null` marks an unknown time or number and `""` an empty string field. The portable implementation is
the `@y2/ontology` package.
## Common fields
| Field | Type | Meaning |
| --- | --- | --- |
| `v` | `0` | Format version |
| `seq` | integer | Position in the log, starting at 1, contiguous |
| `id` | string | Record ID; the prefix names the kind |
| `kind` | `subject`, `designator`, `observation`, `assertion` | Record kind |
| `recorded_at` | time | Record time; strictly increasing along the log |
| `prev` | hex or `null` | `hash` of the previous line; `null` on the first |
| `hash` | hex | SHA-256 of the canonical line without `hash` |
Times are ISO 8601 in UTC with milliseconds, exactly as `Date.prototype.toISOString` prints them.
## Records
### Subject
| Field | Meaning |
| --- | --- |
| `class` | `person`, `organization`, or `system`. A subject never changes class. |
A subject has no name. Names and identifiers arrive as designators and claims.
### Designator
| Field | Meaning |
| --- | --- |
| `designator_kind` | See [Vocabulary](/docs/ontology/reference/vocabulary#designator-kinds) |
| `value` | The canonical string |
| `namespace` | Platform, algorithm, registry, or exchange MIC; `""` for kinds without one |
| `normalized` | Case-folded for names and handles; otherwise equal to `value` |
One designator per `(kind, namespace, normalized)` in a ledger. A second line with the same key is
rejected; reuse the existing ID.
### Observation
| Field | Meaning |
| --- | --- |
| `method` | One passive method, or `analyst-note` |
| `source_grade` | `primary`, `secondary`, or `analytic` (only for a note) |
| `source_url` | The page; required for a retrieval, optional for a note |
| `archive_url` | A preserved copy, or `null` |
| `retrieved_at` | Retrieval time; `null` on a note |
| `content_hash` | SHA-256 of `excerpt`, or of an archived blob when `archive_url` is set |
| `excerpt` | A quote of 1–500 characters, or `null` when an archive holds the content |
| `collector` | A local label for the recorder, 1–80 characters, never a personal name in Y2 |
| `cites` | Observations a note connects; empty on a retrieval |
### Assertion
| Field | Meaning |
| --- | --- |
| `op` | `assert` or `retract` |
| `predicate`, `from`, `to` | The claim; see [predicates](/docs/ontology/reference/vocabulary#predicates) |
| `qualifier` | The public role on `member-of`; `""` otherwise |
| `supersedes` | The current head of the claim key, or `null` on the key's first line |
| `confidence` | Integer 0–100; `null` on a retraction |
| `verification` | A [ladder state](/docs/ontology/reference/vocabulary#verification-ladder); `null` on a retraction |
| `valid_from`, `valid_to` | Valid-time interval, end exclusive; `null` is unknown, never guessed |
| `evidence_refs` | 1–20 earlier observations |
| `note` | Up to 1,000 characters of caveat |
The claim key is `predicate|from|to|qualifier`. Each key has exactly one chain of lines.
## Identifiers
Y2 derives record IDs with its public-ID strategy: a prefix and the first 24 hex characters of
SHA-256 over the record type and the workspace ledger position.
| Kind | Prefix | Example |
| --- | --- | --- |
| Subject | `sbj_` | `sbj_0123456789abcdef01234567` |
| Designator | `dsg_` | `dsg_0123456789abcdef01234567` |
| Observation | `obv_` | `obv_0123456789abcdef01234567` |
| Assertion | `clm_` | `clm_0123456789abcdef01234567` |
The offline format accepts any lowercase ID with the right prefix, such as `sbj_ada` in the
fictional fixtures.
## Rules
Each rule has a code. The API returns the codes in `detail`; the offline checker prints them per line.
| Code | Rule |
| --- | --- |
| `shape.*` | Exact field set and field types for the kind |
| `format.json`, `format.canonical` | The line is JSON in canonical form |
| `chain.seq`, `chain.prev`, `chain.hash` | Contiguous `seq`, `prev` links the previous hash, `hash` matches |
| `chain.time`, `time.format` | Record time strictly increases; times use the exact format |
| `id.format`, `id.duplicate` | Prefix matches the kind; IDs are unique |
| `designator.*` | Canonical value for the kind and namespace; computed `normalized`; one row per key |
| `observation.method`, `observation.grade` | Passive method; only a note is `analytic` |
| `observation.url`, `observation.time` | Exact http(s) URL without userinfo; a retrieval has its URL and time, not after record time |
| `observation.note`, `observation.cites` | A note cites earlier observations and has no retrieval time; a retrieval cites nothing |
| `observation.excerpt`, `observation.hash`, `observation.preservation` | Excerpt 1–500 characters; hash matches the excerpt unless archived; keep an excerpt or an archive |
| `assertion.endpoint` | `from` and `to` match the predicate; distinct ends; same class for `same-as` and `distinct-from` |
| `assertion.qualifier` | Only `member-of` carries a lowercase role |
| `assertion.chain` | First line supersedes nothing; later lines supersede the current head; no double retraction |
| `assertion.evidence` | 1–20 distinct earlier observations |
| `assertion.confidence`, `assertion.interval` | Integer 0–100; `valid_from` before `valid_to` |
| `assertion.verification` | The state is one the evidence supports (see the ladder) |
| `assertion.retract` | A retraction clears confidence, verification, and the interval |
| `assertion.label` | `preferred-label` needs a live `designated-by` for the same pair |
## Example
The first two lines of the fictional fixture, wrapped here for reading:
```json
{"class":"person","hash":"…","id":"sbj_ada","kind":"subject","prev":null,
"recorded_at":"2026-03-01T10:00:00.000Z","seq":1,"v":0}
{"designator_kind":"published-name","hash":"…","id":"dsg_ada_name","kind":"designator",
"namespace":"","normalized":"ada example","prev":"…","recorded_at":"2026-03-01T10:00:01.000Z",
"seq":2,"v":0,"value":"Ada Example"}
```
The complete fixtures, `fictional.ndjson` and `fictional-cyber-market.ndjson`, are in
`packages/ontology/v0/examples` in the Y2 repository.
---
# STIX 2.1 Export (/docs/ontology/reference/stix-export)
A STIX export projects one subject's fold at the requested cursors into an OASIS STIX 2.1 bundle.
It is a projection: the ledger remains the store, and re-exporting the same fold yields the same
object IDs.
The subject neighborhood is bounded. If it exceeds the read budget, the endpoint rejects the
export; export the paginated ledger records and project the complete log offline instead.
## Mapping
| Ledger | STIX 2.1 |
| --- | --- |
| `person` subject | `identity`, `identity_class: individual` |
| `organization` subject | `identity`, `identity_class: organization` |
| `system` subject | `identity`, `identity_class: system` |
| Live preferred label | `identity.name`. An unlabeled subject is named by its ledger ID. |
| Other published names and pseudonyms | `designated_names` in the extension; no `threat-actor` is created to hold aliases |
| `external-id` designations | `identity.external_references` (`sec-edgar-cik`, `lei`, `wikidata`, `mitre-attack`) |
| `ticker` designations | `identity.external_references` with `source_name: ticker` and `MIC:SYMBOL` |
| `email` | `email-addr` |
| `handle` | `user-account` with `account_login` and `account_type` from the namespace; no credential or login history |
| `domain`, `hostname` | `domain-name` |
| `url` | `url` |
| `asn` | `autonomous-system` |
| `designated-by` to an observable | `relationship` of type `designated-by` |
| Other claims | `relationship` with the predicate as its type, `confidence`, `start_time`, and `stop_time` |
| Unconfirmed `same-as` | `relationship` of type `related-to`, described as a hypothesis |
| `follows` | `relationship` of type `follows` between two `user-account` objects |
| Cited observations | `external_references` on the relationship: method as `source_name`, URL, excerpt as `description`, `hashes.SHA-256` |
| Retracted claim | Omitted; with `history=true`, a relationship with `revoked: true` |
`duplicate-of` and `attributed-to` are never emitted: identity is not confirmed by the ledger.
## Extension
Every exported identity and relationship carries a property extension defined in the bundle:
```json
{
"type": "extension-definition",
"spec_version": "2.1",
"name": "Y2 passive entity ledger",
"schema": "https://y2.dev/docs/ontology/reference/stix-export",
"version": "0.1.0",
"extension_types": ["property-extension"]
}
```
| Property | On | Meaning |
| --- | --- | --- |
| `ledger_subject_id` | identity | The `sbj_…` ID |
| `designated_names` | identity | Names other than the label |
| `ledger_claim_id` | relationship | The head line's `clm_…` ID |
| `claim_key` | relationship | `predicate|from|to|qualifier` |
| `predicate` | relationship | The ledger predicate, even when the type is `related-to` |
| `verification` | relationship | The [ladder state](/docs/ontology/reference/vocabulary#verification-ladder) |
| `qualifier`, `note` | relationship | When present |
A `Y2` producer `identity` is the `created_by_ref` of the extension, identities, and relationships.
## Identifiers and timestamps
STIX requires `type--UUID` identifiers. The UUID carries a SHA-256 digest, the same derivation Y2
uses for public IDs, laid out as an RFC 9562 name-based UUID (version 8):
| Object | Digest input |
| --- | --- |
| Cyber observables | The type and the STIX ID-contributing properties, so equal observables share an ID across exports |
| Identities | The type, a one-way per-workspace scope, and the ledger subject ID |
| Relationships | The type, the scope, and the claim key |
The workspace ID is never exported. A relationship's `created` is the first line of its claim key;
`modified` is the head line's record time. An identity's `modified` is the later of its opening and
the record times of its designation and label claims, including retractions.
STIX revocation is permanent. Reasserting a retracted claim starts a new relationship ID and a new
`created` time; later corrections keep that new ID. The extension retains the original claim key.
An explicit `validAt` cursor adds that snapshot time to the identity and relationship ID scope.
STIX has one version clock, so different valid-time pictures cannot share an object version with
different contents. The ledger subject ID in the extension still joins those snapshots. Bundle IDs
digest the full exported content; repeating an unchanged projection yields the same bundle ID.
---
# Ledger Vocabulary (/docs/ontology/reference/vocabulary)
The vocabulary is closed: a value outside these lists is rejected. Terms marked **Y2 profile** extend
the base framework for issuer and supply-chain research and follow the same evidence, verification,
and interval rules.
## Subject classes
| Class | Examples |
| --- | --- |
| `person` | A researcher, an executive, a maintainer |
| `organization` | A company, a vendor, an agency, a threat group identified by its ATT&CK ID |
| `system` | A site, a host, a repository, a service, a model, an autonomous system |
## Designator kinds
| Kind | Value | Namespace |
| --- | --- | --- |
| `published-name` | The name as the page prints it | — |
| `pseudonym` | A name sources present as an alias | — |
| `handle` | The login, without `@` | Platform or host, such as `github.com` |
| `email` | Lowercase addr-spec | — |
| `url` | Exact http(s) URL without userinfo | — |
| `domain`, `hostname` | Lowercase host, no trailing dot | — |
| `asn` | `AS` and a decimal number | — |
| `public-key` | Lowercase hex fingerprint | Algorithm, such as `ssh-ed25519` |
| `external-id` | A registry identifier | `wikidata` (`Q…`), `lei` (20 characters with valid check digits), `attack-group` (`G` and four digits), `sec-cik` (ten digits, **Y2 profile**) |
| `ticker` (**Y2 profile**) | Uppercase listed symbol | ISO 10383 exchange MIC, such as `XNAS` |
## Observation methods
`public-web-page`, `public-profile`, `public-dns`, `certificate-transparency`, `public-archive`,
`public-registry`, `public-code-host`, and `analyst-note`.
Interaction with the subject, authentication, and non-public access have no method name, so they
cannot be recorded as if they were valid sources.
## Predicates
| Predicate | From | To | Meaning |
| --- | --- | --- | --- |
| `designated-by` | subject | designator | The subject is referred to by this identifier |
| `preferred-label` | subject | name designator | Display this name; needs a live `designated-by` |
| `same-as` | subject | subject of the same class | Hypothesis that the rows are one entity |
| `distinct-from` | subject | subject of the same class | Hypothesis that they are not |
| `member-of` | person | organization | A public role, in `qualifier` |
| `affiliated-with` | subject | subject | A weaker public tie |
| `controls` | person or organization | system or designator | Claimed control; stays weak unless a source speaks to control |
| `operates` | person or organization | system | A source says this party runs that system |
| `resolves-to` | designator | designator | A public resolution for the interval observed |
| `follows` | handle | handle | A public page shows one account following another |
| `supplies` (**Y2 profile**) | organization | organization | The first supplies goods or services to the second |
| `subsidiary-of` (**Y2 profile**) | organization | organization | The first is owned or controlled by the second |
`same-as` and `distinct-from` never merge rows. When both are live for a pair, the pair is contested.
## Verification ladder
Verification belongs to a claim, not to a subject.
| State | Honest use | Evidence the ledger requires |
| --- | --- | --- |
| `unverified` | Written down, not checked | At least one observation |
| `mentioned` | The string appeared; no page ties it | A public retrieval |
| `located` | One retrieved page ties them | A public retrieval |
| `self-asserted` | The subject's own page states it | A public retrieval |
| `cross-linked` | Two independent pages identify the link | Retrievals of two different pages |
| `corroborated` | A further independent source agrees | Retrievals of two different pages |
| `contradicted` | A source disputes the link | A public retrieval |
| `stale` | The page is gone or no longer shows it | A public retrieval |
| `analytic` | The only support is an analyst note | Only analyst notes |
A claim supported only by notes is `analytic` or `unverified`.
## Source grades
| Grade | Meaning |
| --- | --- |
| `primary` | The page is the thing itself: the profile, the staff page, the registry entry |
| `secondary` | The page reports on the thing |
| `analytic` | An analyst note connecting other observations; it must cite them |
---
# Tutorial: Map a Company's Cyber-Market Exposure (/docs/ontology/tutorial)
In this tutorial you will record what a public page says about a company, link one of its suppliers,
and read the cyber and market events that reach the company through that supplier. It takes about
ten minutes. You need a workspace with FININT, Cyber, or Explorer access and an owner, admin, or
member role.
We will use a company from the shared catalog. Any organization or vendor works; the examples use
Northwind Example Holdings and its supplier Contoso Example Logistics.
### Open the company
Go to **FININT → Markets → Companies** and select the company. The company page shows its shared
identity, your workspace's financial history, the **Cyber-market exposure** panel, and the
**Entity ledger** panel.
### Open a ledger subject
In **Entity ledger**, choose **Open ledger subject**. Y2 appends a subject line of class
`organization` and anchors it to the catalog company. The subject has no name yet: opening a row
is not a finding. Names and identifiers arrive as claims with evidence.
### Record the company's domain with its source
In **Record an identifier with its source**, enter:
| Field | Value |
| --- | --- |
| Kind | Domain |
| Value | `northwind.example` |
| Public source URL | The company's home page |
| Method | Public web page |
| Excerpt | The sentence on the page that shows the domain |
| Verification | Self-asserted (the company's own page says so) |
| Confidence | 85 |
Choose **Record claim**. Y2 records three lines in one transaction: the `domain` designator, the
observation (source URL, retrieval time, excerpt, and its SHA-256), and a `designated-by` claim
that cites the observation. The domain now appears in the ledger with its verification badge.
### Add a published name and make it the label
Record a second identifier of kind **Published name** from the same page. It appears as a
designation, but the subject still has no display label: a label is its own claim, never a guess.
Choose **Use as label** next to the name. Y2 records an analyst note that cites the page you used,
and a `preferred-label` claim graded `analytic` because its only support is that note.
### Link the supplier
In **Record a supply or ownership tie**, search the shared catalog for the supplier, choose the
role **Supplier of this company**, and cite the page that names it, such as the company's annual
filing on a public registry. Y2 opens a ledger subject for the supplier if your workspace has none,
then records a `supplies` claim from the supplier to the company.
### Read the exposure timeline
Return to the company. **Cyber-market exposure** now lists the supplier on the exposure path with
`ledger · located`, next to any suppliers the shared catalog already knows. CVEs that affect the
supplier, cyber signals about it, and market events about the company appear on one timeline.
When a market event followed a cyber event within seven days, it is listed under **Market events
after cyber events**. That list is timing on a shared path, not a claim of causation.
### Do the same through the API
With a workspace-bound key that holds `ledger:read` and `ledger:write`:
```bash
# Open the subject, anchored to the catalog entity.
curl https://api.y2.dev/api/v2/ledger/subjects \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Idempotency-Key: open-northwind-1" \
--json '{"class":"organization","entityId":"ent_0123456789abcdef01234567"}'
# Record the observation, then the designator, then the claim that cites it.
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--json '{"kind":"observation","observation":{"method":"public-web-page","sourceGrade":"primary","sourceUrl":"https://northwind.example/","retrievedAt":"2026-06-01T11:45:00Z","excerpt":"Northwind Example Holdings — northwind.example","collector":"integration"}}'
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--json '{"kind":"designator","designator":{"kind":"published-name","value":"Northwind Example Holdings"}}'
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--json '{"kind":"claim","claim":{"predicate":"designated-by","from":"sbj_…","to":"dsg_…","confidence":80,"verification":"self-asserted","evidenceRefs":["obv_…"]}}'
```
Then record the label as its own claim, which needs an analyst note that cites the page:
```bash
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--json '{"kind":"observation","observation":{"method":"analyst-note","sourceGrade":"analytic","excerpt":"Display the name the site prints.","collector":"integration","cites":["obv_…"]}}'
curl https://api.y2.dev/api/v2/ledger/records \
--header "Authorization: Bearer $Y2_API_KEY" \
--json '{"kind":"claim","claim":{"predicate":"preferred-label","from":"sbj_…","to":"dsg_…","confidence":60,"verification":"analytic","evidenceRefs":["obv_…note"]}}'
```
Finally, read the fused view with a key that holds `intel:explorer`:
```bash
curl --get "https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/fusion" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "windowDays=90"
```
## What you built
You now have a small, sealed record of what public pages say about a company and one of its
suppliers, with each claim graded by its evidence. Fusion uses those ties to bring the supplier's
cyber exposure onto the company's timeline, and every event on it links back to its source.
Next steps:
- [Correct, supersede, or retract a claim](/docs/ontology/how-to/correct-a-claim) when a page changes.
- [Read the ledger at a point in time](/docs/ontology/how-to/read-at-a-point-in-time) to see what you
believed before a correction.
- [Export STIX and verify the seal](/docs/ontology/how-to/export-and-verify) to share the claims.
---
# Country Intelligence Cards (/docs/osint/country-cards)
Country Intelligence Cards combine country-attributed observations, country instability data,
news, prediction markets, market indices, and curated infrastructure. They
are available with the Situation Room on Lite, Pro, and Elite plans.
## Open a country card
### Open the Situation Room
Go to **OSINT → Monitor** and wait for the map to load.
### Explore the map
Enable the event layers and time range you want to explore. The country card reads its own
country-level data, independently of the map's loaded markers and enabled layers.
### Select a country
Click or tap a country boundary. Y2 opens its card as a modal over the map.
### Close or export
Select **Close**, click the backdrop, or press `Escape` to close the card. Select the download
icon to save the currently assembled dossier as a UTF-8 text file.
Curated country profiles provide fallback scores and a centroid for nearby infrastructure.
Other recognized countries can still display country-attributed observations and available
feeds. A missing infrastructure profile does not imply that news or country data is absent.
## How the card assembles data
```mermaid
flowchart LR
boundary["Selected country boundary"]
baseline["Curated profile
Fallback scores · centroid"]
cii["Country CII
Value · components"]
code["ISO alpha-2 code"]
events["Recent country observations
Signals · timeline · brief context"]
feeds["Country feeds
News · prediction markets · stock"]
refs["Curated registries
Bases · nuclear · ports"]
card["Country card"]
boundary --> code
code --> events --> card
code --> cii
baseline --> card
cii --> card
code --> feeds --> card
refs --> card
```
Selecting a country resolves its ISO alpha-2 code, such as `SA` for Saudi Arabia. Active Signals,
the timeline, and brief context use a bounded query of up to **100 recent, unexpired observations**
attributed to that country. Top News uses a separate query of eligible news and report sources.
Panning, changing map layers, or narrowing the map time range does not filter these country
queries. The card is a recent country dossier, not an exhaustive national event archive.
## Read the instability section
The ring uses the country's current Conflict Indicators Index (CII) when available. The component
breakdown shows country-level values where available and uses curated baselines as fallbacks:
| Component | What the card labels |
| --- | --- |
| Civil unrest | Political and social instability |
| Conflict | Conflict activity from the country event count |
| Security | Security risk |
| Information | Information environment |
If country CII is unavailable, the ring uses an equal-weight average of the four fallback
components. Countries without either country CII or a curated baseline have no measured score;
the displayed fallback is zero.
| Ring value | Ring color |
| --- | --- |
| 0–34 | Green |
| 35–64 | Amber |
| 65–100 | Red |
With country CII, the badge uses that value: below 35 is `NORMAL`, 35–64 is `ELEVATED`, and
65 or above is `CRITICAL`. Without country CII, the badge uses the curated default status,
or `NORMAL` when none exists. A fallback score or status is not a finding of no risk.
## Interpret signals and the timeline
**Active Signals** counts the observations returned by the country query and groups them by event
category. It does not add a separate severity breakdown.
The **7-Day Timeline** limits those observations to events from the last seven days and maps these
categories into four lanes:
| Lane | Included event categories |
| --- | --- |
| Protest | `political` |
| Conflict | `conflict` |
| Natural | `seismic`, `weather`, `fire` |
| Military | `maritime`, `aviation` |
Categories such as economic, health, and cyber can appear in Active Signals but not in a timeline
lane. A busy country can have more than 100 recent observations, so the timeline can omit older
events even when they fall within seven days.
## Read the intelligence brief
When no unexpired brief exists for the country, the card schedules an AI-generated brief. The
generation context contains:
- country name and two-letter code;
- the country CII value, when available;
- the count of observations returned by the country query;
- the first 10 titles from that same set.
The model is instructed to produce five sections in 300–400 words: current situation, military and
security posture, key risk factors, regional context, and outlook. It must acknowledge low activity
instead of inventing threats.
Briefs expire after two hours. Y2 records a context hash based on 10-point CII buckets and
five-event count buckets, but the card reads the newest unexpired brief for the country. A changed
map selection does not force a refresh while that brief remains valid.
The brief is generated from a small, bounded context. Validate important conclusions against the
linked source records before acting on them.
## Country feeds
These widgets use the selected country's ISO alpha-2 code:
### Top News
The card shows up to eight recent, unexpired articles or report observations from RSS, GDELT,
ACLED, News Terminal, and Y2 report sources. It excludes seismic and weather categories and
deduplicates articles for display. Eligibility is applied before the result limit, so satellite
fire detections cannot crowd out available news. Select a headline to open its source URL.
Country selection uses geographic attribution. A story about a Saudi pipeline can appear under
Saudi Arabia; an event on Mayun Island belongs under Yemen. A Red Sea event that merely mentions
Saudi Arabia or affects its shipping does not automatically become a Saudi event. Regional
exposure and mentioned countries are separate context, not additional event locations.
Opening a card reads stored news rather than starting a fresh search. Scheduled ingestion supplies
new observations, and the open card periodically refreshes expiry eligibility. The
[News Signal Feed](/docs/news) can contain related regional items that do not qualify for this
country selection.
If the query fails, **Try again** retries the read. **No recent news** means the query returned no
eligible country news; it does not establish that no real-world event occurred. Check
[source health](/docs/osint/sources) and broader regional feeds when coverage seems incomplete.
For integrations, the [country-news API](/docs/api/osint#read-country-news) returns canonical
observations and preserves their public IDs. Its observation count can differ from the card's
deduplicated headline count.
### Prediction markets
The card selects up to five unexpired Polymarket or Kalshi contracts whose derived country tags
include the selected code. Results are sorted by trading volume and show probability, volume,
optional liquidity and end date, and the provider link. A missing section means that no current
matching contract was found.
### Stock index
For 45 configured countries, the card requests a primary national index from Yahoo Finance when no
valid cache row exists. It displays the index name and percentage change between the latest close
and the close five trading observations earlier. Results expire after one hour. Unsupported codes
or provider failures leave the chip hidden.
## Infrastructure exposure
The card lists the **three nearest** records in each curated registry:
- military bases;
- nuclear facilities;
- strategic ports and chokepoints.
Distance is the great-circle distance from the country's configured centroid. The assets are not
filtered by political boundary, affiliation, or active map layers. “Exposure” therefore means
nearby reference context, not confirmed ownership or presence inside the selected country.
## Download the dossier
The download icon creates a file named
`y2-intel-{iso3}-{YYYY-MM-DD}.txt`. It includes the status, ring and component values, active-signal
counts, and any stock, brief, news, market, and infrastructure data already available when
you select download.
The export is a point-in-time client-side text snapshot. The current card does not provide a
separate PDF or print command.
## Keyboard shortcuts
| Key | Action |
| --- | --- |
| `Escape` | Close the country card |
| `L` | Toggle the map layer sidebar while focus is not in an input |
## Next steps
Explore map layers, regional feeds, and individual observations
Check source schedules, health, and retention
See how observations, specialized caches, and reference overlays differ
---
# Data Infrastructure (/docs/osint/data-infrastructure)
Y2 does not force every map layer into one universal record. It routes data according to its
shape, update rate, and useful lifetime. This distinction matters when you compare source health,
map freshness, search results, and API responses.
## Architecture at a glance
```mermaid
flowchart LR
sources["Scheduled providers
News · hazards · cyber · markets · tracking"]
adapters["Source adapters
Fetch · validate · normalize"]
health["Source control
Circuit state · last success · errors"]
observations["Observation store
Events with time, place, severity, and provenance"]
typed["Specialized caches
Aircraft · vessels · GPS · FININT · markets"]
ontology["Ontology
Sources · entities · places · incidents · relations"]
references["Reference overlays
Bases · cables · satellites · CCTV"]
products["Product views
Situation Room · country cards · search · API"]
sources --> adapters
adapters --> health
adapters --> observations
adapters --> typed
observations <--> ontology
typed --> products
observations --> products
ontology --> products
references --> products
```
The diagram shows three storage paths:
| Path | Best suited to | Examples |
| --- | --- | --- |
| Normalized observations | Time-bound events that can be searched, grouped, and linked | Earthquakes, fires, disaster alerts, threat indicators, news events, Y2-extracted signals |
| Specialized caches | Data with a provider-specific shape or refresh cycle | Aircraft tracks, vessel positions, GPS interference, prediction markets, FININT indicators |
| Reference overlays | Curated context rendered directly by the app | Military bases, nuclear facilities, undersea cables, satellite anchors, CCTV hubs |
A reference overlay can appear on the map without creating a normalized observation or a
source-health record. Its presence does not imply that a provider fetch just succeeded.
## From provider response to observation
### Check the source circuit
Before a scheduled adapter calls its provider, it checks that source's circuit-breaker state.
Five consecutive failures open the circuit for a five-minute cooldown. Other sources continue
independently.
### Normalize the provider payload
The adapter maps provider-specific fields to Y2 concepts such as source, title, category,
severity, event time, coordinates, country, URL, fetch time, and expiry. The original provider
payload may also be retained as source-specific metadata.
### Resolve ontology references
Where the adapter has enough context, Y2 resolves or creates the related data source, place,
entities, and incident. These links are optional: an observation can remain useful before all
ontology references are available.
### Upsert by source identity
Normalized observations use the pair `(sourceType, sourceId)` as their source identity. A later
fetch updates the active observation instead of blindly appending another row. Ambiguous legacy
ownership fails closed rather than overwriting an arbitrary record.
For News Terminal RSS, a reused publisher identifier paired with a different article URL
creates a separate observation. The original article, public identity, and evidence links
remain intact, and the incoming observation retains its provider identity in provenance.
### Recompute dependent intelligence
Writes linked to an incident enqueue corroboration work. Separate scheduled jobs classify
observations, recompute conflict indicators, update statistical baselines, and reconcile stale
incidents and markets.
## Observation and ontology model
An observation is the evidence-bearing event record. It can carry:
- provider identity and raw metadata;
- event time, fetch time, and expiry;
- category, severity, coordinates, region, and country;
- provenance tier: `primary`, `derived`, or `synthesized`;
- temporal meaning: `ongoing`, `past`, `forecast`, or `analysis`;
- links to a canonical data source, place, entities, and incident.
The ontology supplies durable identity around those observations:
```mermaid
erDiagram
DATA_SOURCE ||--o{ OBSERVATION : produces
PLACE ||--o{ OBSERVATION : locates
INCIDENT ||--o{ OBSERVATION : groups
ENTITY }o--o{ OBSERVATION : appears_in
ENTITY ||--o{ ENTITY_RELATION : connects
OBSERVATION ||--o{ RELATION_EVIDENCE : supports
ENTITY_RELATION ||--o{ RELATION_EVIDENCE : has
```
Entity aliases, provider identifiers, and canonical names have dedicated lookup indexes. Merge
redirects preserve references when duplicate objects converge, while integrity jobs audit resolver
indexes and candidate duplicates. Automated destructive entity merging is intentionally
fail-closed until dependent references can be rewritten and verified.
### Identity keys and safe deduplication
Y2 keeps durable object identity separate from the text used to find an object. A Convex document
ID is the stable reference. Names, aliases, and provider identifiers are normalized into dedicated
lookup projections:
| Identity path | Indexed lookup |
| --- | --- |
| Canonical name | entity kind + normalized canonical key |
| Alias | entity kind + normalized alias key |
| Provider identifier | entity kind + provider + normalized provider value |
| Ownership | object type + identity namespace + claim key |
Normalization applies Unicode compatibility normalization, trims and collapses whitespace, and
compares case-insensitively while retaining the original display text. Candidate aliases are
deduplicated before resolution.
These are typed compound indexes, not application-encoded byte keys. Convex owns physical ordering;
Y2 chooses index fields in query order, uses indexed equality/range reads, and paginates large
partitions. Reverse-time queries use numeric timestamps and descending index order instead of
inverted timestamp strings. Opaque Convex IDs are never parsed for time or locality.
An index row is a rebuildable projection, not permission to choose an identity winner. Y2 can
automatically remove a dead, stale, kind-mismatched, or same-owner duplicate projection. If a
normalized key has multiple live owners, the resolver keeps both objects, skips the unsafe alias
transfer, and records one review case. A merge occurs only through the governed reference-rewrite
and verification process.
### Country attribution and regional exposure
A country-filtered observation view uses the observation's geographic attribution. Text-based
resolution prioritizes headline locations and specific places, with matched text and resolution
method retained as provenance. Countries mentioned in the story and countries exposed to a
regional event are recorded separately when available. Those associations do not turn every
regional event into a local event for each affected country.
The News Terminal and country-news views reuse the existing observation, entity, place, and
source model. A topic-feed item may not yet have an eligible, country-attributed observation.
Compare [News feeds](/docs/news) with [country-card selection](/docs/osint/country-cards#top-news)
before treating different result counts as missing data.
For a worked explanation of shared identity and evidence across domains, read
[how Y2 connects cyber, market, and global tracking](/blog/y2-ontology-cyber-market-global-tracking).
## Agent Y2 signals
Completed Y2 reports can be processed into structured observations with extraction context,
resolved entities, places, and incident links. Intel discovery and expansion jobs can create their
own observations through the same ontology layer.
These synthesized records support investigation graphs, evidence links, search, and other
intelligence views. They are not a promise that every future report automatically uses all prior
observations as research context.
## Freshness and retention
Freshness is controlled per data path rather than by one platform-wide timer.
| Data | Typical lifetime or lifecycle |
| --- | --- |
| Most normalized provider observations | Seven-day expiry, cleaned hourly |
| Aircraft tracks and GPS interference | 30 minutes |
| Naval vessel positions | 24 hours |
| Stock index cache | One hour |
| Prediction markets | Two-hour cache expiry, with separate market lifecycle reconciliation |
| FININT indicators | Provider-dependent, from two hours to 45 days |
| User Situation Room snapshots | 90 days |
| Inactive incidents | Automatically resolved after seven days without observations |
Y2 report, intel discovery, and intel expansion observations are excluded from the normal transient
observation cleanup because other evidence records can depend on them. Their own `expiresAt` value
does not make them subject to that hourly deletion path.
API and map queries return the records that remain in their active storage path. If you need a
durable external history, capture the relevant API results in your own governed store instead of
relying on a short-lived Y2 cache.
## How the app reads the data
The Situation Room combines several queries rather than loading one monolithic dataset:
- normalized observations are filtered by source, category, severity, geography, and event time;
- tracking and financial layers read their specialized tables;
- country views combine live calculations with curated country and infrastructure context;
- reference overlays are assembled from app registries and approved external display endpoints;
- the Sources page reads circuit-breaker records separately from map data.
This is why two surfaces can update at different times. For example, a source card can report a
healthy provider while a specialized layer is empty because its rows expired, and a static overlay
can remain visible while no runtime provider call exists for it.
## Next steps
Compare providers, schedules, health semantics, and retention
Learn how the map combines live, cached, and reference layers
Understand the country-level synthesis shown in the map
---
# Situation Room (/docs/osint)
The Situation Room is Y2's map-based workspace for combining current OSINT events, Y2 report
findings, network threats, reference infrastructure, tracking data, live media, and financial
indicators.
Situation Room access requires an active **Lite**, **Pro**, or **Elite** workspace. Free
workspaces see an upgrade wall. All three enabled plans currently receive the same 50-layer
selection limit and one-minute refresh entitlement.
## Open the monitor
### Select Situation Room
Open **Situation Room** in the signed-in app. `/app/osint` redirects to the monitor at
`/app/osint/monitor` after Y2 verifies the active workspace's access.
### Choose a map view
Use the map controls to switch between the 3D globe and 2D Mercator map, and between the
default and satellite basemap. The monitor starts in the global globe view.
### Set the event window
Choose **1H**, **6H**, **24H**, **48H**, **7D**, or **ALL**. The default is 24 hours.
### Select layers
Use the rail on the left to toggle layer entries. Press `L` outside a text field to collapse
or expand the sidebar. New users start with every registered entry selected, and later changes
are saved to their map preferences.
### Inspect the map
Select an event or tracking marker for its source and context. Select a country polygon to
open the country intelligence card. Press Escape to close the selected country.
In either map view, select a grouped marker to open its observation list, then choose an
observation for details. Several observations can share the same coordinates.
## Understand the map controls
### Time range
The time bar changes the minimum timestamp sent to the cached map-event query. It applies to event
markers such as earthquakes, conflicts, weather, fires, and Y2 report events. Static reference
overlays—such as cables, bases, facilities, ports, cameras, and satellite assets—do not become
historical merely because the event window changes. Live tracking tables also use their own
freshness and expiry rules.
### Regions and theaters
The header includes **Global**, six geographic theaters, and two thematic views:
| Type | Views |
| --- | --- |
| Geographic | MENA, Africa, Latin America, Asia-Pacific, Europe, North America |
| Thematic | Energy & Resources, Government / Policy |
Selecting a header view moves the camera to its configured viewport. It does not replace the
active layer selection. The desktop Theater Intel dock separately groups recent events into the
same eight theater definitions. Its headlines follow the selected time range and refresh
independently of map zoom, panning, and enabled layers.
### Loading and refreshing observations
The monitor loads stored observations matching your map area, time range, and enabled event
layers in pages. Progress shows how many observations have loaded. A completed selection means
the matching stored data has loaded; upstream source collection can still be delayed or incomplete.
Panning or zooming within the loaded coverage reuses those observations. Moving beyond it loads
new coverage after the map stops moving. Completed selections refresh after five minutes, and
you can refresh manually sooner. If loading stops, use retry to continue; a failed refresh keeps
the previous completed map visible.
### Location and cursor context
The desktop header can request browser geolocation and center the map on the result. It also shows
the current cursor coordinates. Y2 uses marker, cursor, theater, and time-window context to prepare
queries in the API tool; it does not send browser location until you request it.
## Layer registry
The current registry contains 27 selectable entries across seven groups.
**Y2 Report Events** maps event observations extracted from generated Y2 reports and preserves
report/profile provenance when available.
**Earthquakes**, **Conflicts**, **Political Events**, **Economic Events**, **Weather & Climate**,
**Health & Pandemic**, **Cyber Threats**, **Maritime Events**, **Satellite Fires**, and
**Military Aviation** display categorized rows from the OSINT event cache.
**Live Malware** adds recent URLhaus and Feodo-derived threat observations and connects them to
the Network Intel panel and relationship graph.
**All Satellites** is the aggregate control. Its five subtype entries are **Starlink / Comms**,
**Military / Intel**, **GPS / Navigation**, **Earth Observation**, and **Stations / Telescopes**.
**CCTV Cameras** displays configured public camera assets. Selecting a marker opens the camera
viewer; individual streams can still be unavailable at their upstream source.
**Undersea Cables**, **Nuclear Facilities**, and **Ports & Chokepoints** are reference overlays.
They have minimum zoom thresholds, so zoom in when a selected layer appears empty.
**Aircraft Tracks**, **Naval Vessels**, **GPS Interference**, **Conflict Zones**, and **Military
Bases** combine tracking tables with static reference overlays.
“All Satellites” and its five subtypes are related controls. The 27 count describes selectable
registry entries, not 27 independent upstream feeds.
When every entry is enabled, the map still may not show a marker for every layer. A source can have
no current rows, a row can fall outside the selected time window, a reference layer can require a
closer zoom, or an upstream integration can be unavailable.
## Current tracking status
The tracking status widget appears only when at least one tracking source has usable data or a
recorded aircraft sync timestamp.
| Layer | Current ingestion state |
| --- | --- |
| Aircraft Tracks | The Wingbits aircraft cron has been disabled since May 8, 2026 to stay within the shared request budget. The layer remains registered but automatic aircraft scans are not running. |
| GPS Interference | Wingbits GPS zones run on cadence tiers ranging from hourly hot zones to a daily verification sweep. |
| Naval Vessels | USNI fleet data syncs daily; the upstream fleet report itself updates less frequently. |
Do not interpret an empty Aircraft Tracks layer as proof that no aircraft are present. It
currently reflects disabled scheduled ingestion, not a negative intelligence finding.
## Country cards
Select a country to open its intelligence card. Depending on available data, the card combines:
- A country instability index and recent change.
- An AI-generated or cached country brief.
- A seven-day event timeline and active signals.
- Infrastructure exposure from reference datasets.
- News attributed to the country, prediction markets, and a stock-index indicator.
- A text export assembled in the browser.
Country cards combine static country profiles with live queries, so one section can have data while
another is empty. See [Country Intelligence Cards](/docs/osint/country-cards) for field and scoring
details.
## Desktop analysis panels
The desktop monitor adds controls that are intentionally hidden on screens below the medium
breakpoint.
### Right-side tool rail
Lists countries or regions whose computed instability index is rising, sorted by delta.
Explores recent malware infrastructure and can open related indicators, entities, and network
context.
Searches ontology entities after two characters and opens a live relationship graph for a
selected result.
Builds OSINT area, signal, and report requests from the selected marker, cursor, region, and
time range.
The API Query tool produces URLs and `curl` snippets. It does not bypass authentication. Creating
new API keys and using scoped REST access requires Pro or Elite even though Lite can use the
in-app Situation Room.
### Bottom dock
The resizable desktop dock contains:
- **Live media:** eight configured YouTube news channels, loaded muted by default.
- **Theater Intel:** recent cached events across six geographic and two thematic panels, queried
independently of the map's visible markers. Failed loads or refreshes offer a retry action.
- **FININT:** market, energy, and macro indicators with recent values and detected signals.
These panels are not rendered on mobile; the map, layer selector, time range, markers, tracking
status, cameras, and country card remain the primary mobile surfaces.
## Data and source health
Map data is a mixture of scheduled third-party syncs, Y2-derived observations, typed intelligence
tables, and versioned static reference assets. “Live” means the interface updates reactively when
stored data changes; it does not mean every upstream provider streams continuously or has the same
refresh interval.
Use **Sources** in the monitor header to inspect the source registry, last-success state, record
counts, errors, and dataset-specific notes. See [Intelligence Sources](/docs/osint/sources) and
[Data Infrastructure](/docs/osint/data-infrastructure) before treating freshness across layers as
uniform.
## Entitlements and unfinished management surfaces
Lite, Pro, and Elite currently share these Situation Room entitlement values:
| Limit | Value |
| --- | ---: |
| Selected map layers | 50 |
| Alert rules | 100 |
| Countries per watchlist | 50 |
| Refresh interval | 1 minute |
| Snapshot retention | 90 days |
The backend includes authenticated mutations for watchlists and alert rules and enforces these
limits. The current Situation Room monitor does not expose a watchlist or alert-rule management
screen, so do not rely on the quota table as evidence that those app workflows are available.
Elite additionally enables team seats and audit logs in your workspace. Review
[Plans and Limits](/docs/plans) for the surrounding profile, API, and workspace differences.
## Next steps
Identify which providers and datasets back each monitor surface.
Follow ingestion, normalization, identity, and API representations.
Interpret country-level scores, events, markets, and exports.
Use scoped REST endpoints outside the monitor.
Compare Y2 with Maltego, Flashpoint, Recorded Future, Palantir, and MISP by use case.
---
# Intelligence Sources (/docs/osint/sources)
The Situation Room combines scheduled upstream feeds, event-driven Y2 observations, specialized
tracking and market tables, and curated reference overlays. These source classes have different
refresh and health semantics; a layer name does not imply a continuously streaming provider.
```mermaid
flowchart LR
A[Scheduled provider] --> B[Source adapter]
C[Y2 workflow] --> D[Internal observation]
B --> E[Normalized storage]
D --> E
F[Curated reference asset] --> G[Map overlay]
E --> H[Map, cards, panels, API]
G --> H
B --> I[Circuit-breaker status]
```
## Inspect source status
### Open Situation Room
Open the monitor, then select **Sources** in the header. The page requires the same Lite,
Pro, or Elite Situation Room access as the monitor.
### Identify the source class
**Runtime feeds** display circuit-breaker state. **Map overlays and reference networks** are
catalog entries and do not have the same fetch-health record.
### Read the timestamps and errors
Runtime cards show the last successful fetch, consecutive failure count, current state, last
error when present, and related map-layer IDs.
### Confirm source-specific freshness
Compare the card with the schedules below. A closed circuit means requests are allowed; it
does not prove that every row is recent or that every endpoint inside an aggregate source
succeeded.
## Scheduled upstream feeds
The following jobs are registered in the current Convex cron configuration.
| Source | Stored data | Current schedule |
| --- | --- | --- |
| USGS | Earthquakes | Twice hourly at :05 and :35 |
| GDACS | Global disaster alerts | Twice hourly at :05 and :35 |
| NASA EONET | Natural-event observations | Twice hourly at :05 and :35 |
| OSINT RSS | Categorized headlines from 24 feeds | Twice hourly at :07 and :37 |
| NASA FIRMS | Active fire detections for configured regions | Twice hourly at :10 and :40 |
| Polymarket | Prediction markets | Every 15 minutes |
| Kalshi | Prediction markets | Twice hourly at :24 and :54 |
| URLhaus and Feodo Tracker | Malware URLs and command-and-control infrastructure | Twice hourly at :12 and :42 through a serialized work pool |
| NVD | CVE changes | Every 6 hours at :15 |
| CISA KEV | Known Exploited Vulnerabilities catalog | Daily at 04:20 UTC |
| FRED | Macroeconomic indicators | Twice hourly at :14 and :44 |
| Yahoo Finance markets | Market, commodity, FX, crypto, rate, and volatility indicators | Every 15 minutes at :04, :19, :34, and :49 |
| EIA spot series | WTI, Brent, and Henry Hub series | Hourly at :21 |
| EIA release series | Weekly and monthly energy series | Daily at 14:30 UTC |
| EIA nuclear outages | Facility outage snapshot | Daily at 14:45 UTC |
| EIA grid load | Six US balancing-authority series | Hourly at :33 |
| EIA crude imports | Per-port monthly data | Monthly on day 1 at 15:45 UTC |
| USNI Fleet Report | Naval vessel disposition | Daily at 06:20 UTC |
Schedules describe when Y2 attempts a fetch. The upstream dataset can update more slowly, return
no rows, rate-limit a request, or be unavailable.
## Map result coverage
The monitor loads all matching stored event observations for the visible area, selected time
range, and enabled event layers. It continues through pages automatically; 500 is a page size,
not a total result limit. The displayed count increases while loading, and completion is shown
only after the final page. Changing the area or filters starts a new scan.
Both globe and flat views group nearby observations and show their counts. Select a group to
zoom in; observations at the same location can be selected individually from a paginated list.
Full observation details load when selected.
The time window stays fixed during each scan. A completed view refreshes after five minutes,
or immediately with **Refresh now**. If a refresh fails, the previous completed view remains
visible and **Retry loading** resumes retrieval. A completed scan describes stored observations;
it does not establish complete upstream coverage. FIRMS collection failures and delays are
shown separately. Live tracking, market, malware, and reference overlays retain their own query
and refresh behavior.
## Hazards and earth observation
The adapter reads the USGS earthquake feed for global magnitude 2.5+ observations and stores
magnitude, depth, coordinates, time, and the provider's tsunami indicator. Category and
severity normalization drive the Earthquakes layer.
GDACS contributes global disaster alerts such as earthquakes, cyclones, floods, volcanic
events, and drought-related observations. Provider alert context is normalized into Y2 event
fields.
EONET contributes earth-observation events such as storms, fires, volcanic activity, and
other natural phenomena. Rows can feed the weather and fire views when coordinates and
categories are available.
FIRMS fetches active thermal anomalies for nine configured regional bounding boxes. The
adapter requests today's and yesterday's observations from Suomi-NPP VIIRS, NOAA-20 VIIRS,
NOAA-21 VIIRS, and Terra/Aqua MODIS. Qualifying detections are stored in batches without
truncating each regional result set. Acquisition times are recorded in UTC, and partial
collection failures are reported in source health.
The cache retains new or changed detections for seven days after collection; identical
repeated detections do not extend retention. New collection requests cover
only today and yesterday; they do not recover older gaps or extend coverage beyond those
nine regions. Low-confidence detections are excluded.
Thermal detections are observations, not proof of conflict activity or cause.
## OSINT RSS headlines
The OSINT map pipeline has its own 24-feed registry, separate from the 40-topic News Signal Feed.
It includes:
- Al Jazeera, Middle East Eye, and Al Monitor
- BBC Africa and AllAfrica
- BBC Latin America, InSight Crime, Mexico News Daily, and Reuters Americas
- BBC Asia and South China Morning Post
- BBC Europe and EUobserver
- BBC World
- New York Times World
- Reuters World
- OilPrice
- BleepingComputer and The Hacker News
- WHO News, WHO Disease Outbreaks, ProMED-Mail, ReliefWeb Health, and CDC MMWR
The adapter infers category and severity from source defaults plus headline and description
keywords. It also attempts place and incident resolution only for items that have coordinates.
Feed requests run in batches of five with a ten-second timeout per feed.
The Sources page reports one aggregate `rss` circuit. A sync is treated as successful when at
least one of the 24 feeds succeeds, so a closed RSS circuit does not guarantee that every feed
was available during the last run.
## Cyber intelligence
Y2 stores multiple cyber source types with different cadences and downstream representations.
| Source | What it contributes | Important boundary |
| --- | --- | --- |
| URLhaus | Active malware distribution URLs and related host indicators | Each sync is capped by the adapter and resolved into ontology entities when possible |
| Feodo Tracker | Botnet command-and-control IP or host infrastructure | Shares the serialized cyber ingestion work pool with URLhaus |
| NVD | Vulnerability records updated in the last-modified window | Six-hour delta ingestion; it is not the same as a full NVD backfill |
| CISA KEV | Vulnerabilities known to be exploited | Daily catalog sync; KEV status is an exploitation signal, not a severity score by itself |
URLhaus and Feodo observations power the Live Malware and network-intelligence surfaces. NVD and
CISA KEV also feed the broader Cyber experience and ontology. Free workspaces can use the in-app
Cyber surface, while Situation Room access still requires Lite or higher. New scoped API keys
require Pro or Elite.
## Markets and financial intelligence
### Prediction markets
Polymarket and Kalshi records are normalized into the prediction-market table and also written to
the ontology market model when subject entities can be resolved. Market questions are associated
with countries using provider metadata and configured text matching.
A market probability is a market price, not a verified forecast or Y2 confidence score. Review
liquidity, resolution terms, end date, and source URL before using it as evidence.
### FININT feeds
The desktop FININT panel groups normalized indicators into Markets, Energy, and Macro segments.
FRED, the scheduled Yahoo Finance market adapter, and the EIA family use the cadences in the table
above.
Country cards use a separate Yahoo Finance index adapter. When requested, it fetches the configured
primary index for the selected country, calculates an approximately five-trading-day change, and
caches that result for one hour. That on-demand country-index lookup does not have its own recurring
cron.
In-app FININT is available in Lite, Pro, and Elite. New `intel:finint` API keys require Pro or
Elite; existing Lite keys can retain compatibility limits while the workspace remains on Lite.
## Military and navigation tracking
### Aircraft Tracks
The Wingbits aircraft adapter and map layer remain in the repository, but scheduled aircraft
scans have been disabled since May 8, 2026. Re-enabling requires both cron entries and the
`WINGBITS_AIRCRAFT_TRACKING_ENABLED` environment flag.
The `wingbits` source-health card is shared with GPS-interference ingestion. It can look healthy
because GPS jobs are succeeding even though automatic aircraft tracking remains disabled.
### GPS Interference
Wingbits navigation-accuracy observations are stored as H3 zones with sample count, aircraft
count, average navigation accuracy, and normalized severity. The 22 configured theaters use four
cadence tiers:
| Tier | Cadence | Coverage count |
| --- | --- | ---: |
| Hot | Hourly | 6 theaters |
| Watch | Every 3 hours | 8 theaters |
| Perimeter | Every 6 hours | 5 theaters |
| Verification | Daily | 3 theaters |
GPS zones expire after 30 minutes. A lower-cadence theater can therefore have no current map row
between scans; absence is not evidence that interference stopped.
### Naval Vessels
The USNI adapter ingests fleet-report-derived vessel positions once daily. Stored rows expire
after 24 hours and include vessel class, status, region, strike-group context when present, and a
confidence label. The upstream report itself is not a live AIS stream.
## Internal and recognized source types
The Sources screen also lists identifiers that are not independent scheduled provider feeds:
`y2_report` observations are created from generated Y2 reports and signals. They preserve
report and profile context where available and power the Y2 Report Events layer.
`intel_discovery` and `intel_expansion` observations are created by Y2 intelligence workflows.
They represent derived platform activity rather than third-party source polling.
`manual` identifies operator-entered rows. Its presence in the source-type schema does not
create an ingestion schedule.
`acled`, `gdelt`, and `opensky` remain accepted source identifiers and appear in the in-app
catalog. The current cron file has no scheduled ACLED, GDELT, or OpenSky ingestion job. Treat
their cards as compatibility or planned-source descriptors unless a runtime status row proves
that another in-scope process populated them.
## Reference overlays and media
Reference cards do not use runtime circuit-breaker health. They describe data bundled into or
referenced by the frontend:
- Curated satellite category anchors for the current Space toggles. CelesTrak and SatNOGS are
listed as candidate/reference catalogs; the map is not presenting live orbital propagation from
those providers.
- Curated undersea-cable routes with Submarine Cable Map lineage.
- Y2-maintained strategic ports, military bases, and nuclear-facility inventories.
- Configured public CCTV network assets.
- Eight YouTube live-news channels used by the desktop media panel.
Reference overlays can age independently of runtime feed health. Validate important facility,
route, ownership, and status details against the relevant primary source before operational use.
## Normalization and retention
### Check the circuit
A scheduled adapter first asks whether its source circuit allows a request.
### Fetch and normalize
Source-specific code maps provider fields to typed observations or specialized tracking,
market, vulnerability, and FININT rows. Primary adapters attach their registered data-source ID
and provenance tier when available.
### Resolve context
Where supported, Y2 resolves places, entities, incidents, market subjects, and evidence links.
Resolution failure can be non-fatal, leaving the source observation available without every
ontology link.
### Upsert deterministically
Event-cache rows use the `(sourceType, sourceId)` identity. Duplicate items within a batch
collapse with the last value winning; existing rows are updated rather than blindly appended.
### Publish reactively
Convex queries update monitor markers, cards, panels, and app tools when stored data changes.
Transient `osintDataCache` observations normally receive a seven-day expiry. Evidence-bearing Y2
report, discovery, and expansion observations are excluded from the transient cleanup set.
Specialized tables have independent retention—for example, GPS and aircraft rows use 30 minutes,
vessels use 24 hours, and the country stock-index cache uses one hour.
## Interpret circuit-breaker state
| State | Meaning in the current UI |
| --- | --- |
| Pending | No circuit-breaker row exists for that source type; often means it has never recorded a fetch result |
| Closed | Requests are allowed. The card can still show 1–4 accumulated failures before the threshold opens the circuit |
| Open | Five or more consecutive failures have opened the circuit; requests are skipped until the five-minute cooldown expires |
| Half open | A probe state recognized by the schema and UI; the next successful result closes it and a failed probe reopens it |
After cooldown, an open source is allowed to attempt another request. A success resets the failure
count and last error; another failure keeps the circuit open.
Source health describes adapter execution, not truthfulness, completeness, or corroboration of
the underlying data. Always retain the source URL and provenance when making a consequential
assessment.
## Next steps
Relate each source class to its current map or analysis surface.
Follow identity, ontology, evidence, and API representations after ingestion.
See how source records combine at country level.
Query supported public representations with scoped credentials.
---
# Plans and Limits (/docs/plans)
Y2 resolves feature access and quotas from the active workspace. Switching workspaces can change
the features available in the app and API even when the same person is signed in.
Workspaces have separate plans, resources, API keys, and usage.
Confirm the active workspace before troubleshooting a missing feature or unexpected limit.
## Pricing and workspace model
| Plan | Price | Workspace | Best fit |
|---|---:|---|---|
| Free | Free | 1 seat | Follow one community profile |
| Lite | $5/month or $50/year | 1 seat | Ongoing in-app intelligence and webhook delivery |
| Pro | $20/month or $200/year | 1 seat | Scheduled delivery, API integrations, audio, and advanced publishing |
| Elite | $200/month or $2,000/year | 5 seats, including the owner | Shared intelligence operations with governance controls |
Elite includes 10× Pro’s credits, custom and total profile allowances, Automation definitions,
API keys and request limits, branding templates, and pronunciation dictionaries. These allowances
are shared across the workspace. Elite includes five seats, with larger organizations available by arrangement. Situation Room coverage, refresh
frequency, and retention are the same as Pro. Unlimited delivery remains unlimited.
## Projects and Automations
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Private Projects | No | Yes | Yes | Yes |
| Project graph, timeline, evidence, and knowledge | No | Yes | Yes | Yes |
| Automations | No | No | Up to 10 | Up to 100 |
| Schedule and profile-completion triggers | No | No | Yes | Yes |
| Project-bound Automation output | No | No | Yes | Yes |
| User-bound Project and Automation audit | No | No | No | Yes |
Project access starts with Lite. Automations require Pro or Elite, and their definition quota is
shared by the workspace. Projects and Automation definitions are owner-private in this release;
an Elite workspace does not automatically share one member's Project content with another.
## Profiles and delivery
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Custom profiles | 0 | 1 | Up to 5 custom profiles | Up to 50 custom profiles |
| Community subscriptions | 1 | 3 | Up to 20 total profiles | Up to 200 total profiles |
| Total profiles | 1 | 4 | 20 | 200 |
| Report deliveries | Community subscription only | Unlimited | Unlimited | Unlimited |
| Email delivery | Yes | Yes | Yes | Yes |
| Webhook delivery | No | Yes | Yes | Yes |
| SMS delivery | No | No | Yes | Yes |
| Scheduled reports dashboard | No | No | Yes | Yes |
| Branding templates | 0 | 0 | 10 | 100 |
| Audio narration | No | No | Yes | Yes |
| Pronunciation dictionaries | 0 | 0 | 10 | 100 |
Free includes one community-profile subscription. Creating a custom profile and configuring its
report delivery starts with Lite.
## Agent Y2 and research
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Agent Y2 credits per month | 0 | 100 | 1,000 | 10,000 |
| Web search tool | No | Yes | Yes | Yes |
| Image generation | No | Yes | Yes | Yes |
| Advanced research | No | Yes | Yes | Yes |
| News Terminal | Yes | Yes | Yes | Yes |
## API access
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Create new API keys | No | No | Up to 10 | Up to 100 |
| Default rate limit per minute | — | — | 30 | 300 |
| Default rate limit per day | — | — | 5,000 | 50,000 |
| Webhook management | No | Yes | Yes | Yes |
| x402 pay-per-request | Supported endpoints | Supported endpoints | Supported endpoints | Supported endpoints |
Lite cannot create new API keys. Keys that existed before the Pro requirement retain a
compatibility limit of 10 requests per minute and 500 requests per day while the workspace
remains on Lite.
## Situation Room and Intel workspaces
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Cyber dashboard | Yes | Yes | Yes | Yes |
| Situation Room | No | Yes | Yes | Yes |
| FININT workspace | No | Yes | Yes | Yes |
| Explorer investigation boards | No | Yes | Yes | Yes |
| Investigation sharing | No | No | Yes | Yes |
| Map layers | 0 | 50 | 50 | 50 |
| Alert rules | 0 | 100 | 100 | 100 |
| Watchlist countries | 0 | 50 | 50 | 50 |
| Snapshot retention | — | 90 days | 90 days | 90 days |
## Organization capabilities
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Organization workspace | No | No | No | Yes |
| Workspace members | 1 | 1 | 1 | 5 included; expansion by arrangement |
| Workspace audit logs | No | No | No | Yes |
| Workspace API-key oversight | No | No | No | Yes |
## Choose the next guide
Set up a topic, cadence, and delivery path within the active workspace limits.
Deliver reports to an HTTPS endpoint on Lite, Pro, or Elite.
Configure Pro or Elite credentials and scopes for API requests.
Understand workspace switching, resource ownership, and Elite team governance.
Organize chats, evidence, private knowledge, graphs, and reports on Lite or higher.
Run bounded Agent Y2 work from Pro and Elite workspace triggers.
---
# Branding Templates (/docs/profiles/branding)
Branding templates let a Pro or Elite workspace reuse its logo, colors, terminology, and
delivery copy across intelligence profiles.
Branding requires an active **Pro** or **Elite** workspace. Pro supports up to **10**
templates; Elite supports up to **100**.
## Create and assign a template
### Open Branding
In the app, go to **InfoOps → Branding**, then select **New Template**.
### Name the template
Use a name that identifies where the branding belongs, such as “Company Briefings” or
“Client Deliverables.” A template name can contain up to 50 characters.
You can also mark the template as the workspace default. Only one template can hold that
marker at a time.
### Configure the output
Add only the elements you need. Any omitted values fall back to Y2 defaults.
| Area | Available settings |
| --- | --- |
| Visual identity | Logo, logo alt text, header color, accent color |
| Terminology | Report type and analyst role |
| Delivery copy | Email preview prefix, SMS prefix, audio intro, audio outro |
| Static content | Optional content before or after a report |
### Save the template
Resolve any validation errors, then select **Save**.
### Assign it to a profile
Create or edit a profile, open **Advanced**, select the branding template, and save the
profile. Branding is applied when that profile is rendered or delivered.
The **Set as Default** marker is unique, but the current profile editor still requires you to
select a branding template explicitly. Verify the assignment in **Advanced** instead of
relying on the default marker alone.
## Visual identity
### Logo
The branding editor accepts PNG, JPEG, SVG, and WebP files up to 2 MB. Add meaningful alt text
for recipients who cannot see the image.
| Property | Enforced limit |
| --- | --- |
| File size | 2 MB |
| Logo alt text | 100 characters |
Use a wide logo with a transparent background when possible, then preview a delivered email to
confirm that it remains legible at small sizes.
### Colors
Header and accent colors accept three- or six-digit hex values, such as `#05c` or `#0055cc`.
Values are normalized when the template is saved.
Use only hex colors. Invalid values are rejected by server-side validation.
## Terminology and delivery copy
Use `{name}` in supported text fields to insert the assigned profile's name.
For pronunciation rules that apply to names, acronyms, or technical terms, use a
[pronunciation dictionary](/docs/audio#create-a-pronunciation-dictionary).
## Add static content
Each template can place one content block before the report and another after it. A block can
use the `callout`, `highlight`, or `sponsor` style and can contain channel-specific copy.
| Content | Enforced limit | Used by |
| --- | ---: | --- |
| Label | 30 characters | Block heading |
| HTML | 500 characters | Email and rendered report |
| Audio text | 200 characters | Audio narration |
The `{name}` placeholder is supported in the label, HTML, and audio text.
Server-side limits control what can be saved. Keep static HTML at or below 500 characters,
audio text at or below 200, and labels at or below 30 even if an editor counter displays a
larger allowance.
Static HTML is sanitized before it is stored and rendered. Scripts, embedded forms, event
handlers, unsafe URLs, iframes, and similar executable elements are removed. Use simple
formatting and links rather than interactive markup.
## Update or remove a template
Assigned profiles reference the template rather than copying it. Changes therefore affect
subsequent renders and deliveries from every profile using that template.
To delete a template:
### Find assigned profiles
Edit each profile that uses the template.
### Remove or replace the assignment
Select another template or clear the branding selection, then save the profile.
### Delete the template
Return to **InfoOps → Branding**, select **Delete**, and confirm.
The backend rejects deletion while any profile still references the template. Unassign every
profile first, even if the confirmation dialog suggests that profiles will fall back
automatically.
## API boundary
The public profile create and update schemas expose `brandingTemplateId`. Use the generated
[Profiles API reference](/docs/api/reference/profiles) for the canonical request schema and
required scopes.
Creating, editing, and deleting branding templates is currently an app-only workflow; dedicated
branding-management endpoints are not part of the public OpenAPI document.
## Plan limits
| Plan | Templates | Logo upload |
| --- | ---: | --- |
| Free | 0 | No |
| Lite | 0 | No |
| Pro | 10 | Yes |
| Elite | 100 | Yes |
## Next steps
Configure a profile and assign the template.
Review how generated reports reach recipients.
Compare workspace capabilities and quotas.
---
# Create a Profile (/docs/profiles/creating)
Use the profile editor to define a recurring intelligence question, structure its reports, and
configure how Y2 researches and schedules it.
Sign in, complete onboarding, and select the workspace that should own the profile. Profile
capacity belongs to the active workspace; see [Plans and limits](/docs/plans).
## Editor workflow
The editor validates each step before moving forward. On desktop, the report preview updates as
you change the profile.
## Create the profile
### Open the editor
Open [My Profiles](/app/infoops/my-profiles), then select **Create Profile**. You can also open
the [new profile editor](/app/infoops/profiles/new) directly.
### Complete Basic Information
Enter the fields shown in the first editor step:
Write the topic as a monitoring objective rather than a category name:
```text
Monitor newly disclosed ransomware campaigns affecting US healthcare providers,
including exploited vulnerabilities, named threat actors, and operational impact.
```
The editor may display a three-layer option, but report execution currently caps recursion
at the root topic plus one child layer. Select the two-layer option for the deepest effective
workflow.
### Design the report structure
Choose a **General**, **Finance**, **Technology**, **Security**, or **Custom** template. Reorder,
rename, add, or remove sections until the preview matches the decision the report should
support.
Add **Custom Instructions** when the topic alone does not express the required emphasis, tone,
comparison, or exclusions.
```text
Separate confirmed exploitation from unverified reporting. Prioritize patient-care impact,
affected vendors, and mitigations with named owners or deadlines.
```
Switching from a custom section list to a preset replaces the custom sections after
confirmation.
### Configure schedule and research
In **Advanced Configuration**, review these settings:
Choose **Daily**, **Weekly**, **Biweekly**, or **Monthly**, then select the required day and
time. The editor stores and displays schedule times in UTC.
Choose Basic or Advanced search, select a time range from one day to one year, and
optionally add comma-separated domains to include or exclude.
**Auto** lets the model decide when to use its active research tool. **Required** requires
tool use. **None** disables tool calls for the run.
Source Freshness applies the current recency preference and maximum-age settings. Link
Validation checks whether selected links are accessible.
Pro and Elite can enable audio narration, choose a voice and speed, apply a pronunciation
dictionary, and select a branding template. Unavailable controls remain disabled on lower
plans.
Turn **Share with Community** off when the profile should remain workspace-only. When it is
on, other users can discover and subscribe to the profile.
### Create and verify the profile
Select **Create Feed**. Y2 creates the profile as active, schedules its next run, creates the
workspace's subscription record, and returns you to **My Profiles**.
Confirm that:
- The profile appears in **My Profiles**.
- Its name, topic, and cadence match the editor.
- Its status is active.
- The profile opens without an access or plan-limit error.
### Review delivery after creation
Open the saved profile's delivery settings. New profile subscriptions use email when no other
method is selected. Webhooks require Lite or higher; SMS requires Pro or Elite and a verified
phone number.
Delivery preferences are tied to the subscription, so they become available after the profile
exists.
## Example configurations
```yaml
name: Weekly Cyber Risk Brief
topic: >-
Monitor ransomware campaigns affecting US healthcare providers, including exploited
vulnerabilities, threat actors, and operational impact.
tags: [cyber, ransomware, healthcare]
template: Security
frequency: Weekly
researchLayers: 2
timeRange: Week
```
```yaml
name: Critical Supplier Risk Monitor
topic: >-
Monitor disruption risks affecting semiconductor suppliers, East Asia ports, rare-earth
inputs, and export-controlled components.
tags: [supply-chain, semiconductors, logistics]
template: General
frequency: Daily
researchLayers: 2
includeDomains: [reuters.com, cisa.gov]
```
## Troubleshooting
Make the name at least three characters and the topic at least ten characters. Validation
messages appear below the affected field.
Confirm the active workspace and compare its current profile count with
[Plans and limits](/docs/plans). Limits include owned profiles and subscriptions.
Confirm that the active workspace includes the feature. Audio, branding, and SMS require Pro
or Elite; webhook delivery starts with Lite.
Narrow the topic to a geography, industry, entity set, time horizon, or decision. Use Custom
Instructions for output constraints and domain controls for source boundaries.
## Next steps
Understand UTC timing, frequency-specific fields, and schedule changes.
Verify the default delivery path and report format.
Deliver completed reports to an HTTPS endpoint.
Review visibility, subscriber behavior, and ownership before publishing a profile.
---
# Information Operations (/docs/profiles)
An Information Operations profile is the durable configuration behind a recurring Y2 briefing. It
defines what to research, how to structure the result, when to run, and where the report should go.
## Default report writing
Y2's default writing guidance follows the
[Basset-Hound OSINT writing standards](https://github.com/Basset-Hound-OSINT/osint-resources/blob/main/knowledge/reporting/writing-standards.md):
lead with the assessment and why it matters, distinguish sourced facts from judgments, explain
confidence and its evidence basis, and address material alternatives, assumptions, and gaps.
Reports use neutral language, unambiguous dates, and inline citations; Y2 adds source references.
Your saved sections and custom instructions take precedence over the default layout, tone,
language, and requested length. Detailed section guidance, bullets, and examples are passed to
synthesis together. Existing profiles keep their saved structures, and optional domain templates
remain available. The engine's HTML, citation, and minimum-completeness requirements still apply.
Writing guidance directs generation; it is not a guarantee that each model judgment is correct.
## Profile lifecycle
```mermaid
flowchart LR
config["Topic, instructions, and research settings"] --> run["Research workflow"]
run --> report["Structured intelligence report"]
report --> workspace["Y2 workspace"]
report --> delivery["Enabled delivery channels"]
report -. "context for later cycles" .-> run
```
The profile persists between runs. Updating its topic, instructions, source-quality settings, or
cadence changes later reports without requiring a new workflow each time.
## What a profile controls
A topic, tags, and optional custom instructions define the question and desired emphasis.
A template or custom section list defines how the result is organized. New profiles start
with Bottom Line Up Front, Key Judgments, Background, Analysis, and Confidence Assessment.
Search depth, time range, domain controls, source freshness, link validation, and recursive
research tune how evidence is gathered.
Daily, weekly, biweekly, or monthly timing controls when the profile runs. The active plan
determines which delivery methods are available.
## Profile types
Subscribing to a global or community profile does not transfer ownership. The original owner keeps
the profile configuration; the subscriber controls their own subscription and supported delivery
preferences.
Profile ownership and subscriptions count against the active workspace. Free includes one
community-profile subscription but no custom profiles. Creating a custom profile starts with
Lite. See
[Plans and limits](/docs/plans) for the full entitlement reference.
## Research depth and source quality
Y2 supports a single-pass workflow and recursive research. When recursion is enabled, the runtime
can identify subtopics and research them in parallel before final synthesis.
Report generation currently caps recursive depth at one child layer: the root topic plus one
layer of parallel subtopic research. A larger saved value does not create additional runtime
layers.
Source-quality settings can prefer recent results, apply a maximum source age, and validate links.
These controls improve evidence selection, but they do not guarantee that an external source is
complete or correct.
## Report structure
Profiles can use a general, finance, technology, or security-oriented structure, or a custom list
of sections. The first required section establishes the main conclusion; later sections add
findings, analysis, risks, outlook, or other domain-specific context.
Audio narration and pronunciation dictionaries are separate Pro and Elite capabilities. Branding
templates can apply a consistent visual identity and terminology to supported reports.
## Choose the next task
Configure a topic, report structure, research behavior, cadence, and initial delivery.
Understand frequencies, UTC scheduling, and changes to future runs.
Make an eligible personal profile discoverable to the community.
Apply reusable visual identity and report terminology on supported plans.
Choose narration voices and pronunciation dictionaries for generated reports.
---
# Schedule Profile Reports (/docs/profiles/scheduling)
Every active custom profile stores a UTC cron schedule. Y2 uses it to enqueue the next report run
after the current run reaches a terminal state.
## Supported frequencies
| Frequency | Current schedule behavior | Required input |
|---|---|---|
| Daily | Every day at the selected UTC time | Time |
| Weekly | Every selected weekday at the selected UTC time | Weekday and time |
| Biweekly | On the 1st and 15th of each month at the selected UTC time | Time |
| Monthly | On the selected day of the month at the selected UTC time | Day of month and time |
The current Biweekly schedule runs on calendar days 1 and 15. It is not a rolling fourteen-day
interval and does not use the weekly day selector.
A monthly schedule runs only when the selected date exists. For example, a profile scheduled for
the 31st does not run in a thirty-day month.
## Set a schedule
### Open the profile editor
Open [My Profiles](/app/infoops/my-profiles), select the profile, and choose its edit action.
### Open Advanced Configuration
Move to the third editor step. Find **Delivery Schedule** at the top of the page.
### Choose a frequency
Select Daily, Weekly, Biweekly, or Monthly. Weekly adds a weekday selector; Monthly adds a
day-of-month selector.
### Choose the UTC time
Select an hourly time from `00:00` through `23:00`. The editor and backend interpret the value
as UTC.
Convert the desired local time to UTC before saving. Repeat the conversion when your local
timezone enters or leaves daylight saving time.
### Save and verify
Select **Update Feed**, return to **My Profiles**, and confirm that the profile displays the
expected next-run information.
Changing the schedule of an active profile cancels its existing scheduled job, enqueues the new
schedule, and starts a report workflow immediately. Make schedule changes when an extra run is
acceptable.
## Generated cron values
Y2 generates the cron expression; the profile editor does not accept a custom expression.
## Choose an effective cadence
Use for topics where a missed day changes the decision: active incidents, market movement, or
operational disruptions.
Use for recurring reviews where developments should accumulate into a manageable briefing.
Use when twice-monthly calendar reporting is acceptable and strict fourteen-day spacing is not
required.
Use for slower trend reviews, executive summaries, or stable watch areas.
When several profiles serve the same reader, stagger their UTC times. This spreads research work
and prevents multiple reports from arriving together.
## Failure and recovery behavior
Report workflows use bounded action retries and a recovery watchdog for eligible interrupted or
stalled runs. A failed synthesis does not proceed to audio or delivery as if it were complete.
Recovery behavior does not change the profile's stored cadence.
## Next steps
Verify where a completed report is sent by default.
Send completed reports to an HTTPS endpoint.
Return to the full editor workflow and research settings.
---
# Share a Profile (/docs/profiles/sharing)
Turn on **Share with Community** to make a workspace-owned profile discoverable to other Y2 users.
Subscribers follow the same profile; sharing does not create a separate copy for each workspace.
The current new-profile editor starts with **Share with Community** enabled. Turn it off in
Advanced Configuration before selecting **Create Feed** when the profile should remain private.
## How sharing works
```mermaid
flowchart LR
owner["Owning workspace"] --> profile["Shared profile configuration"]
profile --> catalog["Community discovery"]
catalog --> subscriptions["Subscriber workspace records"]
profile --> reports["Future profile reports"]
reports --> subscriptions
```
The owning workspace controls the profile topic, research configuration, cadence, status, and
community visibility. Subscriber workspaces control their own subscription and supported delivery
preferences.
Changes to the shared profile affect later reports for every active subscriber because subscribers
reference the same profile.
## Before sharing
Confirm that the profile:
- Has a clear name and a topic that another reader can understand without private context.
- Uses report sections and instructions appropriate for a broader audience.
- Does not contain secrets, customer data, private names, or confidential domain restrictions.
- Has produced a report you reviewed for scope, source quality, and usefulness.
- Uses a cadence the owning workspace intends to maintain.
Treat the profile name, topic, tags, custom instructions, and research configuration as public
metadata when community sharing is enabled. Do not use a shared profile as a secret store.
## Share an existing profile
### Open the profile editor
Open [My Profiles](/app/infoops/my-profiles), select a profile owned by the active workspace,
and choose its edit action.
### Review the public-facing configuration
Check the name, topic, tags, section titles, and custom instructions. Remove internal-only
details before changing visibility.
### Enable community sharing
Open **Advanced Configuration** and turn on **Share with Community**.
### Save and verify
Select **Update Feed**, then open [Community](/app/infoops/community). Search for the profile
name and confirm that it appears with the expected Global or Community classification.
## What subscribers can do
Subscribers can:
- Discover and open global or community profiles.
- Add a profile to the active workspace when capacity remains.
- Remove their own subscription without changing the shared profile.
- Configure a supported delivery method for their subscription.
They cannot edit or delete the owning workspace's profile.
Subscription limits belong to the active workspace: Free supports one external community or global
subscription, Lite supports three, and Pro or Elite count subscriptions and owned profiles against
their total profile capacity. See [Plans and limits](/docs/plans).
## Stop sharing
### Edit the shared profile
Open the profile from **My Profiles** and move to Advanced Configuration.
### Disable community sharing
Turn off **Share with Community**, then select **Update Feed**.
### Confirm discovery is disabled
Search for the profile in **Community**. It should no longer be available for a new
subscription.
Disabling community sharing removes the profile from public discovery, but it does not
automatically delete subscription records that already exist. Deleting the profile removes its
subscriptions and associated generated resources; treat deletion as a separate destructive
action.
## Maintain a useful shared profile
Make focused refinements. Replacing the subject entirely can surprise existing subscribers.
Add concise domain, geography, industry, and cadence tags that improve discovery.
Check whether sources and conclusions still match the published scope before expanding it.
Y2 does not currently promise an automatic subscriber notification for profile edits. Avoid
silent changes that materially alter the briefing.
## Next steps
Understand the discovery and subscription experience from the reader's perspective.
Refine the topic, sections, source controls, and research behavior.
Choose where completed reports should arrive.
---
# Create Your First Project (/docs/projects/getting-started)
This tutorial creates a Project in the active workspace, attaches one existing resource, and
starts a chat whose context stays inside that Project.
## Before you begin
You need:
- A Lite, Pro, or Elite active workspace.
- An owner, admin, or member role with write access. Elite viewers are read-only.
- At least one report, profile, graph board, or generated image in the same workspace if you want
to attach an existing resource.
## 1. Confirm the active workspace
Use the workspace switcher and select the workspace that should own the Project. Plan access and
resource choices are resolved from this workspace.
## 2. Create the Project
1. Open **Projects** in the app navigation.
2. Select **New Project**.
3. Enter a short name.
4. Add an objective that describes the decision or investigation the Project should support.
5. Optionally add standing instructions for Project-scoped AI work.
6. Save the Project.
The new Project starts active with a private context policy. Its initial workspace view opens the
Graph, Timeline, Situation, Evidence, Knowledge, and Overview tabs.
## 3. Attach an existing resource
Open **Overview**, search the resource picker, and attach a report, profile, graph board, or
generated image. The picker shows only resources owned by you in the active Project workspace.
Attaching a report automatically preserves its stored citations as Project evidence. Attaching a
profile does the same for every prior report in that profile and keeps preserving citations from
future reports. Supported report intelligence can also schedule a graph projection.
## 4. Start a Project chat
Select **New chat** from the Project. Messages written in that thread carry the Project and
workspace identifiers. If you later switch workspaces, Y2 hides the thread until its workspace is
active again.
## 5. Verify the result
Return to **Overview** and confirm that:
- The resource appears under Project resources.
- The thread appears under recent Project chats.
- Timeline activity records the changes.
Create a Project-bound [Automation](/docs/automations/creating) when the objective needs a
scheduled or report-completion follow-up. Lite Projects remain fully usable without Automations.
---
# Projects (/docs/projects)
Projects are private working contexts for a single intelligence objective. A Project connects
related chats, reports, profiles, graph boards, generated images, evidence, and private documents
without mixing them into another workspace or the global knowledge corpus.
Projects use the active workspace's plan. Free workspaces can see the upgrade path but cannot
read or change Project data.
## The Project boundary
Every Project has both a `workspaceId` and an `ownerId`. In this release, that means:
- The Project can use only resources owned by the same user in the same active workspace.
- Project chats and private knowledge stay attached to that Project.
- Switching workspaces removes access until the Project's workspace is active again.
- Other Elite workspace members do not automatically receive access. Project sharing is not yet
supported.
Y2 checks the boundary again for background indexing, graph projection, chat writes, and
Automation runs. A saved identifier alone does not grant access after membership or plan access
changes.
## Project views
| View | Purpose |
|---|---|
| Graph | Maintain a persistent graph for the objective. |
| Timeline | Review attached intelligence and Project activity in time order. |
| Situation | Explore geolocated signals attached to the Project. |
| Evidence | Preserve citations, captures, notes, tags, and lineage. |
| Knowledge | Add private source documents for Project-scoped retrieval. |
| Overview | Review attached resources, recent chats, and eligible Automations. |
Project knowledge is separate from Global Knowledge. Removing a Project binding archives that
binding; it does not publish the content globally.
## Projects and Automations
Projects are available starting with Lite. [Automations](/docs/automations) require Pro or Elite.
A Lite user can still use every Project view, but the Automation card presents the Pro upgrade
path instead of querying protected Automation data.
On Pro or Elite, an Automation can be bound to a Project. Its output thread and run context remain
inside the same owner and workspace boundary.
## Elite governance
Elite workspaces add user-bound audit records for material Project changes, including Project
creation and settings changes, resource and evidence lifecycle actions, graph changes, chat moves,
and tracking-rule changes. Audit records identify the acting user and workspace; they do not make
private Projects visible to other members.
## Choose the next task
Build a Project and add its first intelligence resource.
Attach reports, preserve evidence, add knowledge, and move chats safely.
Review access rules, supported resources, views, limits, and lifecycle behavior.
---
# Organize Intelligence in a Project (/docs/projects/organizing-intelligence)
Use this guide when a Project already exists and you need to keep its supporting material
organized without crossing workspace boundaries.
## Attach reports and profiles
Open **Overview**, use the resource search, choose a role, and attach the item. Supported resource
types are reports, profiles, graph boards, and generated images. Y2 rejects an item that belongs to
another owner or workspace, even if its identifier is supplied directly.
Use roles consistently:
- **Knowledge** for background material.
- **Working** for active analysis.
- **Output** for deliverables.
- **Primary graph** for the main graph board.
## Preserve and classify evidence
Open **Evidence** to preserve citations from linked reports, capture a web source, add notes and
tags, or mark an item important. Evidence lifecycle history records archive, restore, and prune
actions. Trackers can classify future evidence by exact domain or URL prefix.
Pruning is intentionally stricter than archiving. Review downstream lineage before pruning a
source that supports an assertion, report, graph object, or incident.
## Add private knowledge
Open **Knowledge** and upload PDF, text, Markdown, Word, PowerPoint, Excel, OpenDocument, RTF, EPUB,
or CSV files up to 10 MB. PDFs must contain selectable text because scanned pages requiring OCR are
rejected. Uploaded content is indexed only for authorized Project-chat retrieval; it is not added
to the global knowledge corpus. Archive a binding when the source should stop participating in
retrieval, and restore it when needed.
## Maintain the Project graph
Open **Graph** to create the persistent graph, or save an Explorer graph into the Project. Saving
again merges nodes and edges by identifier. A Project graph supports up to 250 nodes and 500
edges.
## Move an existing chat
Move only a chat you own in the active workspace. The target Project must also be active and owned
by you. Once moved, future message access and background Agent Y2 work are checked against the
Project boundary.
## Archive completed work
Archive the Project when the objective is complete. Archiving removes it from active lists and
blocks active-only operations without deleting its history. Restore it from the archived Projects
view when work resumes.
---
# Projects Reference (/docs/projects/reference)
## Availability and access
| Rule | Behavior |
|---|---|
| Minimum plan | Lite |
| Workspace scope | The active workspace must match the Project workspace. |
| Ownership | Owner-private in this release; team sharing is not supported. |
| Write roles | Owner, admin, or member. Elite viewers are read-only. |
| Context policy | `project_only` |
| Elite audit | Material changes include the workspace and acting user. |
## Project fields
| Field | Constraint or meaning |
|---|---|
| Name | Required, up to 80 characters. |
| Objective | Optional, up to 1,000 characters. |
| Instructions | Optional, up to 8,000 characters. |
| Status | `active` or `archived`. |
| Default view | Graph, Timeline, Situation, Reports, Evidence, or Knowledge. |
| Default time window | 24 hours, 7 days, 30 days, or custom. |
| Revision | Used to reject conflicting settings updates. |
## Supported resource types
| Resource | Ownership check |
|---|---|
| Investigation graph board | Same owner and workspace. |
| Profile | Same owner and workspace. |
| Report | Its profile must have the same owner and workspace. |
| Generated image | Same owner and workspace. |
Resource roles are `knowledge`, `working`, `output`, and `primary_graph`.
## View and storage limits
- Persistent graphs: 250 nodes and 500 edges.
- Recent Project chat list: the app displays up to 12 threads.
- Project instructions: 8,000 characters.
- Graph title: 200 characters.
- Private knowledge accepts PDF, text, Markdown, Word, PowerPoint, Excel, OpenDocument, RTF, EPUB,
and CSV files up to 10 MB.
- PDFs are extracted page by page and must contain selectable text; scanned pages requiring OCR are
rejected.
- Private knowledge is capped at 100 documents per owner and workspace, 200 PDF pages, 2,000,000
extracted characters, and 512 indexed chunks per revision.
- Evidence uses a separate archive and integrity contract; evidence attachments are not parsed as
Project knowledge.
## Lifecycle
Archiving is reversible. Project resources, chat association, evidence history, graph state, and
private knowledge remain stored. Active-only mutations reject archived Projects until restored.
Deleting an attached resource link does not delete the original report, profile, graph board, or
image. Evidence pruning and knowledge-source deletion have separate confirmation and retention
behavior in their respective views.
---
# Use Y2 from the Command Line (/docs/sdks/cli)
Use Y2 Information Dominance for interactive agent work and one-off prompts. Use curl for
reproducible API checks, pagination, exports, and JSON selection.
## Install the native harness
The installer supports macOS and Linux on x86-64 and arm64. It downloads the matching release,
verifies its SHA-256 checksum, and installs `y2` under `~/.y2/bin` by default.
The initial macOS CLI archives are SHA-256 verified native binaries, but they are not Developer
ID signed or Apple notarized. The signed and notarized Apple release is intentionally deferred.
```bash title="Terminal"
export PATH="$HOME/.y2/bin:$PATH" && curl -fsSL https://y2.dev/harness/install.sh | sh
```
This one command makes `y2` available in the current terminal and adds the same PATH entry to your
detected shell profile for future terminals. Pass `--no-modify-path` to leave shell profiles
unchanged. Verify the binary:
```bash title="Terminal"
export PATH="$HOME/.y2/bin:$PATH"
y2 --version
```
Pin an exact release when you need a reproducible install:
```bash title="Terminal"
export PATH="$HOME/.y2/bin:$PATH" && curl -fsSL https://y2.dev/harness/install.sh | sh -s -- v0.0.7
```
Upgrade an installed harness on the stable channel:
```bash title="Terminal"
y2 upgrade
```
## Save a Y2 API key
Create a key with the `agent:y2` scope. New API keys require a Pro or Elite workspace plan.
`y2 auth` opens Y2 API Keys in your browser, prints the URL as a fallback, and securely prompts
for the key:
```bash title="Terminal"
y2 auth
```
The harness stores the key in its supported local credential backend. Run `y2 setup` when you
already have a key and want to enter it without opening the browser. For ephemeral shells and CI,
set the key in the environment instead:
```bash title="Terminal"
export Y2_API_KEY="y2_..."
```
Keep the key out of shell history, logs, prompts, tool arguments, and browser bundles.
## Run Agent Y2
Start an interactive session from the project you want the harness to use as its workspace:
```bash title="Terminal"
cd your-project
y2
```
Run one noninteractive prompt with `y2 ask`:
```bash title="Terminal"
y2 ask "Summarize the changes in this repository"
```
The CLI sends its local tool schemas through Agent Y2's isolated harness mode. It discovers
applicable `AGENTS.md` files from the workspace root down to each target file, makes skills and
subagents available to the model, and keeps file and command execution on your machine behind the
configured permission policy. Y2's API returns tool calls; it does not execute the CLI's local tools
on the server.
Agent Y2 is the default route. Inspect the active configuration and local preflight without
printing the key:
```bash title="Terminal"
y2 status --json
y2 doctor
```
To use another OpenAI-compatible endpoint directly, provide its base URL, key, and model:
```bash title="Terminal"
export OPENAI_BASE_URL="https://your-provider.example/v1"
export OPENAI_API_KEY="your-provider-api-key"
export Y2_MODEL="your-model-id"
y2
```
`OPENAI_BASE_URL` may include `/chat/completions`; otherwise y2 appends it. See the
[Agent Y2 API guide](/docs/api/agent-y2) for the default endpoint and streaming contract.
## Use curl directly
You need curl and, for JSON processing, jq:
```bash
curl --version
jq --version
export Y2_API_KEY="y2_..."
```
New API keys require Pro or Elite. The key must carry the scope required by each operation.
Shell history, debug tracing, process listings, and CI output can expose secrets. Keep the key in
the environment, quote variable expansions, and do not use `set -x` in credentialed scripts.
## Create a reusable GET helper
```bash title="y2.sh"
#!/usr/bin/env bash
set -euo pipefail
: "${Y2_API_KEY:?Set Y2_API_KEY before running this script}"
y2_get() {
local path="$1"
curl --fail-with-body --silent --show-error \
"https://api.y2.dev${path}" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
}
y2_get "/api/v1/news/feeds" | jq '.meta'
```
The helper accepts origin-relative API paths such as `/api/v1/news` and
`/api/v2/incidents`. That also makes it safe to pass a pagination link returned by Y2.
## Discover News topics
List all current topic IDs and names:
```bash
y2_get "/api/v1/news/feeds" |
jq -r '.data[] | [.id, .name, .groupLabel] | @tsv'
```
Then request a bounded page:
```bash
y2_get "/api/v1/news?topics=markets,macro&limit=10" |
jq -r '.data[] | [
.publishedAt,
(.sentiment.label // "unknown"),
.title,
(.links.canonical // "")
] | @tsv'
```
Current News rows use `title`, `summary`, and nested `sentiment` fields. Use the feed directory
instead of assuming an older crypto-only topic list.
## Inspect profile and subscription IDs
`GET /profiles` returns paired resources. Print both IDs before scripting updates:
```bash
y2_get "/api/v1/profiles" |
jq -r '.data[] | [
.profile.id,
.subscription.id,
.profile.name,
.subscription.delivery.method
] | @tsv'
```
Use `prf_...` for profile configuration and report filtering. Use `sub_...` when changing delivery.
## Create a profile idempotently
```bash
profile_id=$(
curl --fail-with-body --silent --show-error \
"https://api.y2.dev/api/v1/profiles" \
--request POST \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: supplier-risk-2026-07-21" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting strategic semiconductor suppliers.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00"
}' |
jq -r '.data.id'
)
printf '%s\n' "$profile_id"
```
The response ID is the profile's canonical `prf_...` ID. Reuse the same idempotency key only with
the same request body.
## Paginate every report for a profile
Y2 returns an opaque continuation link. Follow it unchanged:
```bash title="export-report-index.sh"
#!/usr/bin/env bash
set -euo pipefail
: "${Y2_API_KEY:?Set Y2_API_KEY}"
: "${PROFILE_ID:?Set PROFILE_ID to a prf_ ID}"
next="/api/v1/reports?profileId=$PROFILE_ID&limit=20"
while [ -n "$next" ]; do
page=$(
curl --fail-with-body --silent --show-error \
"https://api.y2.dev${next}" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
)
jq -r '.data[] | [.id, .publishedAt, (.summary // "")] | @tsv' <<<"$page"
next=$(jq -r '.links.next // empty' <<<"$page")
done
```
Do not decode or edit the cursor. It is bound to the original filters and ordering.
## Export one report as Markdown
```bash
: "${REPORT_ID:?Set REPORT_ID to an rpt_ ID}"
curl --fail-with-body --silent --show-error \
"https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/markdown" \
--output "$REPORT_ID.md"
```
The file contains the report body directly. It is not a JSON envelope.
## Process NDJSON one row at a time
News and several list endpoints can return newline-delimited JSON:
```bash
curl --fail-with-body --silent --show-error --get \
"https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/x-ndjson" \
--data-urlencode "topics=cybersecurity" \
--data-urlencode "limit=50" |
while IFS= read -r row; do
jq -r '[.publishedAt, .title] | @tsv' <<<"$row"
done
```
For multi-page ingestion, also capture `X-Y2-Next-Cursor` from the response headers and send it as
the next request's `cursor` value.
## Capture status, headers, and errors
Use temporary files when a script needs to branch on HTTP status while retaining Problem Details:
```bash
body_file=$(mktemp)
headers_file=$(mktemp)
trap 'rm -f "$body_file" "$headers_file"' EXIT
status=$(
curl --silent --show-error \
--output "$body_file" \
--dump-header "$headers_file" \
--write-out '%{http_code}' \
"https://api.y2.dev/api/v1/reports/not-a-report-id" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
)
if [ "$status" -ge 400 ]; then
jq '{status, code, detail, requestId}' "$body_file" >&2
grep -i '^Retry-After:' "$headers_file" >&2 || true
exit 1
fi
```
Log `requestId`, not credentials. Retry safe reads or idempotent writes only, and follow
`Retry-After` on `429`.
## Call Intel v2
Intel paths use `/api/v2` directly:
```bash
y2_get "/api/v2/incidents?limit=20" |
jq -r '.data[] | [.id, .severity, .title] | @tsv'
```
Do not place `/api/v1` before an Intel v2 path.
## Know when curl is not enough
curl can inspect the initial `402` and `PAYMENT-REQUIRED` header, but it does not create an
EIP-3009 signature by itself. Use an x402 v2-capable wallet client, then retry with the generated
`PAYMENT-SIGNATURE`.
Agent Y2 uses SSE in the Vercel AI SDK data-stream format. curl can display raw frames, but an
application should use a parser that understands the stream and retains `X-Thread-Id`.
curl is useful for management endpoints, but testing delivery requires a public HTTPS receiver
that preserves raw request bytes for signature verification.
---
# Client Libraries and Tools (/docs/sdks)
Y2 publishes an OpenAPI 3.1 contract that can drive types, generated clients, request tools, and
tests. Treat that contract—not a package's convenience method names—as the compatibility boundary.
New API-key access requires Pro or Elite. Existing grandfathered Lite integrations retain their
existing keys during the grace period. Create a key in the active workspace and grant only the
scopes the client needs.
## Choose an integration style
Generate path and schema types, then call Y2 with a small typed `fetch` wrapper.
Build a synchronous or asynchronous HTTP client around the published response contract.
Install Y2 Information Dominance for agent workflows, or use curl and jq for direct API work.
Explore path and query parameters, run live reads, and copy starter snippets.
| Option | Best for | Contract fidelity | Main responsibility |
| --- | --- | --- | --- |
| Y2 Information Dominance | Interactive agent work and one-off prompts | Uses Agent Y2 or a configured OpenAI-compatible endpoint | Keep the harness and credentials current |
| Generated OpenAPI client | Broad API coverage | Regenerate when the spec changes | Review generator behavior |
| Generated types + native HTTP | Controlled production integrations | Types come directly from OpenAPI | Maintain a small transport layer |
| curl or Workbench | Exploration and operations | Request is visible and explicit | Handle parsing and pagination |
| Separately released package | Convenience APIs | Depends on its release date | Verify version and operation coverage |
Package installation names, exported classes, and helper methods are release-specific and are not
defined by the platform OpenAPI file. Before adopting a separately released client, compare its
generated operation IDs and models with the current endpoint reference.
## Start from the contract
Download the production description:
```bash
curl --fail --silent --show-error \
"https://y2.dev/api/openapi.yaml" \
--output openapi.yaml
```
Then validate the assumptions that most often break generated clients:
- v1 paths inherit `https://api.y2.dev/api/v1`;
- Intel v2 operations override the server with `https://api.y2.dev`;
- bearer scopes are declared in `x-required-scopes`;
- supported reads can declare x402 as a second security mode;
- errors use `application/problem+json` with stable top-level `code` and `requestId`;
- list pagination uses `links.next` and `meta.page.nextCursor` where applicable; and
- some endpoints can return NDJSON, GeoJSON, Markdown, plain text, audio redirects, or SSE.
## Use a minimal bearer request
Every bearer client needs the same basic transport behavior:
```bash
export Y2_API_KEY="y2_..."
curl --fail-with-body \
"https://api.y2.dev/api/v1/news?topics=markets&limit=5" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
```
Keep the API key on a server, worker, job runner, or local development machine. Do not ship it in a
browser bundle or mobile application.
## Model the common response shapes
```mermaid
flowchart TD
A[HTTP response] --> B{Status 2xx?}
B -->|No| C[Problem Details]
B -->|Yes| D{Representation}
D -->|Collection| E[data array + meta + links]
D -->|Single JSON| F[data object + optional meta]
D -->|Streaming or file| G[Endpoint-specific transport]
```
Do not assume every successful response is `{ data: [...] }`. A generated client must preserve
declared media types and status codes, especially `204` deletes, `302` audio redirects,
`text/event-stream`, `application/x-ndjson`, `application/geo+json`, and `text/markdown`.
## Regenerate safely
### Pin the input
Generate from a reviewed copy of `https://y2.dev/api/openapi.yaml` or pin the generated output
in source control. Avoid silently regenerating from a moving URL during every production build.
### Review operation IDs and servers
Stable operation IDs become method names in many generators. Confirm that v2 paths do not
receive the v1 prefix.
### Add transport policies
Configure timeouts, bounded retries, rate-limit handling, idempotency keys for supported writes,
and secret management outside generated files.
### Smoke-test one operation per representation
Test ordinary JSON, pagination, a mutation, and any special representation your integration
consumes. Generated types alone do not prove deployed runtime behavior.
## Coverage checklist
Use the generated reference to verify the exact operation rather than inferring coverage from a
client namespace.
| Surface | Special handling to verify |
| --- | --- |
| Profiles | Separate `prf_` profile and `sub_` subscription resources; PUT versus PATCH |
| Reports | Compact defaults, bounded includes, Markdown/text/audio representations |
| News | Topic catalog, JSON or NDJSON pagination, cache-readiness errors |
| OSINT | JSON, NDJSON, and GeoJSON; source availability varies |
| Intel v2 | Operation-level server override and ontology IDs |
| Webhooks | Idempotency, ETags, write-only secrets, and `204` deletion |
| Agent Y2 | API-key-only SSE transport, not an ordinary JSON response |
| x402 | Payment challenge and settlement headers outside normal bearer auth |
---
# Build a Python Client (/docs/sdks/python)
This guide builds a dependency-free Python client around Y2's public HTTP contract. Use it directly
for small services and jobs, or use the same behaviors as acceptance criteria for a generated client.
Load `Y2_API_KEY` from the environment or a secret manager. Notebook output, exception dumps,
and committed `.env` files can leak credentials as easily as application logs.
## 1. Configure the environment
The examples use Python 3.11 or newer and only the standard library:
```bash
export Y2_API_KEY="y2_..."
python --version
```
New keys require a Pro or Elite workspace and the scopes needed by the operations you call.
## 2. Create the transport
```python title="y2_client.py"
from __future__ import annotations
import json
import os
from dataclasses import dataclass
from typing import Any, Mapping, TypeVar, cast
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
T = TypeVar("T")
API_ORIGIN = "https://api.y2.dev"
API_V1 = f"{API_ORIGIN}/api/v1"
class Y2ApiError(Exception):
def __init__(
self,
status: int,
problem: dict[str, Any] | None,
retry_after: str | None,
request_id: str | None,
) -> None:
detail = problem.get("detail") if problem else None
super().__init__(detail or f"Y2 request failed with HTTP {status}")
self.status = status
self.problem = problem
self.retry_after = retry_after
self.request_id = request_id
@dataclass(frozen=True)
class Y2Client:
api_key: str
timeout_seconds: float = 30.0
@classmethod
def from_env(cls) -> Y2Client:
api_key = os.environ.get("Y2_API_KEY")
if not api_key:
raise RuntimeError("Y2_API_KEY is required")
return cls(api_key=api_key)
def request_json(
self,
method: str,
path_or_url: str,
*,
query: Mapping[str, str | int | float | bool | None] | None = None,
body: Mapping[str, Any] | None = None,
headers: Mapping[str, str] | None = None,
) -> T:
if path_or_url.startswith("https://"):
url = path_or_url
elif path_or_url.startswith("/api/"):
url = f"{API_ORIGIN}{path_or_url}"
else:
url = f"{API_V1}{path_or_url}"
params = {
key: str(value).lower() if isinstance(value, bool) else value
for key, value in (query or {}).items()
if value is not None
}
if params:
separator = "&" if "?" in url else "?"
url = f"{url}{separator}{urlencode(params)}"
request_headers = {
"Authorization": f"Bearer {self.api_key}",
"Accept": "application/json",
**(headers or {}),
}
data = None
if body is not None:
request_headers.setdefault("Content-Type", "application/json")
data = json.dumps(body, separators=(",", ":")).encode("utf-8")
request = Request(url, data=data, headers=request_headers, method=method)
try:
with urlopen(request, timeout=self.timeout_seconds) as response:
raw = response.read()
if response.status == 204 or not raw:
return cast(T, None)
return cast(T, json.loads(raw))
except HTTPError as error:
raw = error.read()
problem = None
if raw:
try:
decoded = json.loads(raw)
if isinstance(decoded, dict):
problem = decoded
except json.JSONDecodeError:
pass
raise Y2ApiError(
error.code,
problem,
error.headers.get("Retry-After"),
error.headers.get("X-Request-Id"),
) from error
```
This helper handles JSON and `204 No Content`. Create separate response readers for Markdown, plain
text, NDJSON, GeoJSON, audio redirects, and SSE instead of forcing every media type through
`json.loads`.
## 3. Read current News items
```python title="news.py"
from typing import Any, TypedDict
from y2_client import Y2Client
class NewsPage(TypedDict):
data: list[dict[str, Any]]
meta: dict[str, Any]
links: dict[str, str | None]
client = Y2Client.from_env()
page: NewsPage = client.request_json(
"GET",
"/news",
query={"topics": "markets,macro", "limit": 10},
)
for item in page["data"]:
print(item["title"], item["url"])
```
Use `GET /news/feeds` to discover the current 40-topic catalog. Current items use `title`, `summary`,
and a nested `sentiment.label`/`sentiment.value` object—not older top-level `signal` and sentiment
scalar fields.
## 4. Create a profile safely
```python title="profiles.py"
from typing import Any
from y2_client import Y2Client
client = Y2Client.from_env()
created: dict[str, Any] = client.request_json(
"POST",
"/profiles",
headers={"Idempotency-Key": "supplier-risk-2026-07-21"},
body={
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting strategic semiconductor suppliers.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00",
},
)
profile_id = created["data"]["id"]
print(profile_id) # prf_...
```
Creation returns the profile in `data`. It also creates an active subscription, but retrieve that
separate `sub_...` resource from `GET /profiles` when you need to change delivery.
Use `PATCH /profiles/{profileId}` for partial changes. `PUT` replaces the complete mutable state,
and omitted optional values reset or clear. Retain returned `ETag` headers if your workflow uses
`If-Match`; a JSON-only wrapper that discards headers should be extended for that workflow.
## 5. Follow report pagination
```python title="reports.py"
from collections.abc import Iterator
from typing import Any
from y2_client import Y2Client
def reports_for_profile(
client: Y2Client,
profile_id: str,
) -> Iterator[dict[str, Any]]:
next_url: str | None = "/reports"
first = True
while next_url:
page: dict[str, Any] = client.request_json(
"GET",
next_url,
query={"profileId": profile_id, "limit": 20} if first else None,
)
yield from page["data"]
next_url = page["links"]["next"]
first = False
client = Y2Client.from_env()
for report in reports_for_profile(client, "prf_0123456789abcdef01234567"):
print(report["id"], report["summary"])
```
Treat `links.next` and its cursor as opaque. Do not decode, edit, or combine a continuation link
with different filters.
## 6. Run independent reads concurrently
The standard-library transport is synchronous. For a small job, run independent calls in worker
threads without sharing mutable client state:
```python title="concurrent_reads.py"
import asyncio
from y2_client import Y2Client
async def main() -> None:
client = Y2Client.from_env()
news, feeds = await asyncio.gather(
asyncio.to_thread(
client.request_json,
"GET",
"/news?topics=markets&limit=10",
),
asyncio.to_thread(client.request_json, "GET", "/news/feeds"),
)
print(len(news["data"]), len(feeds["data"]))
asyncio.run(main())
```
For a high-throughput service, replace the transport with an async HTTP client that supports
connection pooling, but keep the same authentication, timeout, error, pagination, and media-type
rules.
## Handle Problem Details
```python title="errors.py"
from y2_client import Y2ApiError, Y2Client
client = Y2Client.from_env()
try:
client.request_json("GET", "/reports/not-a-report-id")
except Y2ApiError as error:
code = error.problem.get("code") if error.problem else "UNKNOWN"
print(error.status, code, error.request_id)
if error.status == 429:
print("Retry after", error.retry_after)
```
Retry only safe reads or writes protected by an idempotency key. A `404` on tenant-owned resources
can intentionally avoid revealing whether another tenant owns the ID.
## Call Intel v2 with the correct base
Intel path keys already contain `/api/v2`, and their OpenAPI operation overrides the top-level v1
server:
```python
incidents = client.request_json(
"GET",
"https://api.y2.dev/api/v2/incidents",
query={"limit": 20},
)
```
Do not prepend `https://api.y2.dev/api/v1` to an Intel v2 path.
## Evaluate a generated Python client
If you generate models and operation methods from OpenAPI, verify that the result:
- honors operation-level server overrides;
- represents nullable and optional fields separately;
- exposes `application/problem+json` rather than replacing it with generic exceptions;
- preserves response headers for rate limits, ETags, request IDs, and x402;
- handles `204` and `302` without trying to decode JSON; and
- provides explicit streaming APIs for NDJSON and Agent Y2 SSE.
A generated package can reduce boilerplate, but its published method names and release cadence are
separate from the Y2 API contract. Compare its generated operation IDs with the current endpoint
reference before adopting it.
---
# Build a TypeScript Client (/docs/sdks/typescript)
Use generated OpenAPI types with native `fetch` when you want current schema coverage without
depending on release-specific SDK method names. This approach works in Node.js, Bun, serverless
functions, and workers with standards-compatible fetch APIs.
Do not instantiate a bearer client in browser-delivered code. Put Y2 calls behind your own
server, worker, or API route so `Y2_API_KEY` never enters a public bundle.
## 1. Generate declarations
Install the same generator used by the Y2 platform repository:
```bash
bun add --dev openapi-typescript@^7.13.0
bunx openapi-typescript https://y2.dev/api/openapi.yaml \
--immutable \
--alphabetize \
--output src/y2-api.d.ts
```
```bash
npm install --save-dev openapi-typescript@^7.13.0
npx openapi-typescript https://y2.dev/api/openapi.yaml \
--immutable \
--alphabetize \
--output src/y2-api.d.ts
```
Commit the generated declaration or pin the OpenAPI input in your build pipeline. Regeneration from
a moving production URL should be an intentional compatibility update, not an invisible deploy step.
Add a repeatable script:
```json title="package.json"
{
"scripts": {
"generate:y2-types": "openapi-typescript https://y2.dev/api/openapi.yaml --immutable --alphabetize --output src/y2-api.d.ts"
}
}
```
## 2. Create a JSON transport
Set the credential in the runtime environment:
```bash
export Y2_API_KEY="y2_..."
```
Then create a transport that preserves Y2 Problem Details and rate-limit context:
```typescript title="src/y2-client.ts"
import type { components } from "./y2-api";
const API_ORIGIN = "https://api.y2.dev";
const API_V1 = `${API_ORIGIN}/api/v1`;
export type Y2Problem = components["schemas"]["ErrorResponse"];
export class Y2ApiError extends Error {
constructor(
readonly status: number,
readonly problem: Y2Problem | null,
readonly retryAfter: string | null,
readonly requestId: string | null,
) {
super(problem?.detail ?? `Y2 request failed with HTTP ${status}`);
}
}
function isProblem(value: unknown): value is Y2Problem {
return (
typeof value === "object" &&
value !== null &&
"status" in value &&
"code" in value &&
"detail" in value
);
}
function resolveUrl(pathOrUrl: string): string {
if (pathOrUrl.startsWith("https://")) return pathOrUrl;
if (pathOrUrl.startsWith("/api/")) return `${API_ORIGIN}${pathOrUrl}`;
return `${API_V1}${pathOrUrl}`;
}
export async function y2Json(
pathOrUrl: string,
init: RequestInit = {},
): Promise {
const apiKey = process.env.Y2_API_KEY;
if (!apiKey) throw new Error("Y2_API_KEY is required");
const headers = new Headers(init.headers);
headers.set("Authorization", `Bearer ${apiKey}`);
if (!headers.has("Accept")) headers.set("Accept", "application/json");
if (init.body && !headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
const response = await fetch(resolveUrl(pathOrUrl), {
...init,
headers,
signal: init.signal ?? AbortSignal.timeout(30_000),
});
const text = await response.text();
let payload: unknown = null;
if (text) {
try {
payload = JSON.parse(text);
} catch {
payload = text;
}
}
if (!response.ok) {
throw new Y2ApiError(
response.status,
isProblem(payload) ? payload : null,
response.headers.get("Retry-After"),
response.headers.get("X-Request-Id"),
);
}
return payload as T;
}
```
`y2Json` is intentionally JSON-only. Use separate functions for NDJSON, Markdown, plain text, audio
redirects, and Agent Y2 SSE so a content-type mismatch cannot be silently cast to a JSON model.
## 3. Type a read operation
Extract parameter and response types from the path entry:
```typescript title="src/news.ts"
import type { paths } from "./y2-api";
import { y2Json } from "./y2-client";
type ListNewsQuery = NonNullable<
paths["/news"]["get"]["parameters"]["query"]
>;
type ListNewsResponse =
paths["/news"]["get"]["responses"][200]["content"]["application/json"];
export async function listNews(query: ListNewsQuery): Promise {
const params = new URLSearchParams();
if (query.topics) params.set("topics", query.topics);
if (query.limit !== undefined) params.set("limit", String(query.limit));
if (query.cursor) params.set("cursor", query.cursor);
if (query.format) params.set("format", query.format);
return y2Json(`/news?${params}`);
}
const page = await listNews({ topics: "markets,macro", limit: 10 });
for (const item of page.data) {
console.log(item.title, item.url);
}
```
Use `GET /news/feeds` to discover the current 40-topic catalog. Current items use `title`, `summary`,
and a nested `sentiment.label`/`sentiment.value` object—not older top-level `signal` and sentiment
scalar fields.
## 4. Type a mutation
Profile creation returns a `PublicProfile` directly in `data`. Its ID is `data.id`; profile creation
also creates a subscription, but that `sub_...` ID is obtained from `GET /profiles`.
```typescript title="src/profiles.ts"
import type { paths } from "./y2-api";
import { y2Json } from "./y2-client";
type CreateProfileBody =
paths["/profiles"]["post"]["requestBody"]["content"]["application/json"];
type CreateProfileResponse =
paths["/profiles"]["post"]["responses"][201]["content"]["application/json"];
export async function createProfile(
body: CreateProfileBody,
idempotencyKey: string,
): Promise {
return y2Json("/profiles", {
method: "POST",
headers: { "Idempotency-Key": idempotencyKey },
body: JSON.stringify(body),
});
}
const created = await createProfile(
{
name: "Critical Supplier Risk",
topic: "Monitor disruption affecting strategic semiconductor suppliers.",
frequency: "daily",
scheduleTimeOfDay: "08:00",
},
"supplier-risk-2026-07-21",
);
console.log(created.data.id); // prf_...
```
Use `PATCH /profiles/{profileId}` for partial changes and `PUT` only for full replacement. Preserve
the `ETag` returned by create or update when you need `If-Match` concurrency protection.
## 5. Paginate reports
Follow the response link instead of decoding or modifying an opaque cursor:
```typescript title="src/reports.ts"
import type { paths } from "./y2-api";
import { y2Json } from "./y2-client";
type ReportsPage =
paths["/reports"]["get"]["responses"][200]["content"]["application/json"];
export async function* reportsForProfile(profileId: string) {
let next: string | null = `/reports?profileId=${encodeURIComponent(profileId)}&limit=20`;
while (next) {
const page: ReportsPage = await y2Json(next);
yield* page.data;
next = page.links.next;
}
}
for await (const report of reportsForProfile("prf_0123456789abcdef01234567")) {
console.log(report.id, report.summary);
}
```
The list and default detail response are compact. Request `include=content,sources` or
`Accept: text/markdown` only when the consumer needs those representations.
## Call Intel v2 correctly
The OpenAPI path key includes the full `/api/v2` prefix, and the operation overrides the document
server with `https://api.y2.dev`:
```typescript
type IncidentList =
paths["/api/v2/incidents"]["get"]["responses"][200]["content"]["application/json"];
const incidents = await y2Json(
"https://api.y2.dev/api/v2/incidents?limit=20",
);
```
Do not compose Intel URLs by adding `/api/v1` to the path.
## Handle special representations
Request `application/x-ndjson`, read `response.body` as a stream, split complete newline-delimited
records, and retain `X-Y2-Next-Cursor`. Do not call `response.json()` on the whole stream.
Send `Accept: text/markdown` to `GET /reports/{reportId}` and use `response.text()`. The response
is the report body, not a `{ data }` envelope.
Agent Y2 returns `text/event-stream` in the Vercel AI SDK data-stream format. Use an SSE-aware
streaming consumer and retain `X-Thread-Id`; do not route it through `y2Json`.
## Production checklist
- Pin or review generated declaration changes.
- Give the key only the required scopes.
- Use idempotency keys on supported create operations.
- Follow `Retry-After` on `429` and retry only safe or idempotent requests.
- Log `X-Request-Id` without logging credentials or full payment payloads.
- Treat `404` as non-disclosing for tenant-owned resources.
- Test special media types separately from JSON.
---
# Vision (/docs/vision)
Y2's vision is an intelligence workspace where every decision-maker can maintain relevant context,
understand what changed, and act without rebuilding the research process from scratch.
This page explains the design direction of Y2. For current product availability, use
[Plans and limits](/docs/plans) and the [Changelog](/docs/changelog).
## The intelligence loop
```mermaid
flowchart LR
sources["Open sources and operator inputs"] --> context["Connected intelligence context"]
context --> research["Human and agent research"]
research --> outputs["Briefings, alerts, graphs, and APIs"]
outputs --> decisions["Operator decisions"]
decisions -. "new priorities" .-> context
```
The goal is not a larger stream of information. It is a durable loop in which new evidence updates
shared context, research produces an inspectable result, and operator priorities shape the next
cycle.
## Design direction
### Persistent rather than one-off
A question that matters today will often matter again tomorrow. Profiles and saved workspace
context should make recurring monitoring easier than restarting the same search.
### Connected rather than isolated
Reports, events, entities, sources, and investigations should reinforce one another. Readers should
be able to move from a summary to the evidence, relationships, and prior context behind it.
### Human-directed agents
AI should extend an analyst's reach while preserving user control. The operator defines the goal,
reviews the evidence, and decides what action—if any—to take.
### Usable wherever decisions happen
Intelligence should work in the Y2 interface and through delivery channels, APIs, and interoperable
contracts. A useful result should not be trapped in one screen.
### Compounding community knowledge
Reusable profiles and shared intelligence patterns can reduce duplicated work. Community knowledge
becomes more valuable when its authorship, scope, and source context remain understandable.
## What remains constant
- Operators control the topics, workspace, and delivery paths that matter to them.
- Source context and limitations remain visible enough to support independent judgment.
- Product capabilities are documented as they exist, separately from long-term direction.
- Integrations use explicit contracts so teams can build without depending on hidden behavior.
## Continue exploring
Understand the access and decision-support problem Y2 is designed to address.
Compare available workspace features and enforced limits.
Follow shipped improvements and current release notes.
---
# InfoOps Report Workflow (/docs/workflows)
Every active InfoOps profile runs through a fixed, durable report workflow. The workflow searches
the web, optionally expands the topic into child searches, synthesizes one report, enriches it,
delivers it to subscribers, and schedules the next run.
This page documents the built-in report pipeline configured through an InfoOps profile. Pro and
Elite users can also create [Automations](/docs/automations) that run bounded Agent Y2 work on a
schedule or after a profile report completes.
```mermaid
flowchart LR
A[Profile trigger] --> B[Root web research]
B --> C{Recursive layer?}
C -->|Enabled| D[2-3 parallel child searches]
C -->|Disabled| E[Final synthesis]
D --> E
E --> F[Optional audio]
F --> G[Required enrichment]
G --> H[Subscriber delivery]
H --> I[Update state and schedule next run]
```
## How a run starts
A report run is attached to a profile, not created as an independent workflow object. Y2 queues a
run when:
- An active custom profile is created.
- A paused or cancelled profile becomes active.
- An active profile's status or schedule changes.
- Its next scheduled execution time arrives.
- Recovery logic restarts a failed or stale run.
The start guard skips inactive profiles, prevents a second live run for the same profile, and
defers new starts while a retry is already scheduled. Creating an active profile therefore starts
an initial report immediately and also schedules its next normal occurrence.
The workflow engine runs for active profiles on every plan. Plan-specific profile, delivery,
audio, API, and workspace limits still apply to the surrounding features.
## Research and synthesis
### 1. Search the root topic
Y2 builds a search query from the profile topic and topic-specific hints, then calls Tavily. The
research action honors these saved search fields:
The search action validates freshness using the engine's default topic-aware rules. When it finds
stale sources and still has more than three results, it removes stale URLs before synthesis.
The profile's **Source Freshness** and **Link Validation** values are stored, but the current
research action uses its centralized default freshness configuration rather than those saved
switches. Domain and time-range controls above are applied directly.
### 2. Expand one child layer
When recursion is enabled, an AI step proposes two or three concise, search-ready subtopics. Y2
researches those child topics in parallel and merges successful findings and unique source URLs
with the root results. A failed child does not discard successful root or sibling research.
The engine is breadth-first and caps effective recursion at one child layer. It uses a 480-second
overall research budget and reserves up to 120 seconds for the child batch before final synthesis.
The current profile editor offers 1-, 2-, and 3-layer labels, but it always saves recursion as
enabled. At runtime, an enabled depth of `0` defaults to `1`, and values above `1` are capped at
`1`. The three app choices therefore execute at most one child layer. A public API profile with
`recursionConfig.enabled: false` is the current way to request a true root-only run.
The saved `strategy` value is accepted by profile schemas, but the current engine always runs the
implemented breadth-first path.
### 3. Synthesize one report
Only the root layer performs synthesis. Y2 combines root and child findings, deduplicates sources,
and truncates the combined research document at 35,000 characters before generating one report.
The synthesis prompt includes:
- The profile topic and up to 2,000 characters of custom instructions.
- The saved report format, including supporting instructions, or the default BLUF structure.
- Shared OSINT writing guidance for sourced judgments, confidence, alternatives, and evidence gaps;
user formatting instructions take precedence over its presentation defaults.
- Resolved branding terminology.
- A compact summary of up to three previous reports for continuity.
- A structured SIGINT contract and the discovered source URLs.
Y2 rejects incomplete synthesis that lacks H2 sections, ends abruptly, or contains no
usable signals. A failed quality check stops the workflow before audio or delivery rather than
sending a partial report.
## Models and provider routing
The app does not expose a report-model selector. The current subtopic and synthesis cascades are
fixed in the workflow code:
| Stage | Primary | Fallback order |
| --- | --- | --- |
| Subtopic identification | `z-ai/glm-5.3-flash` | `google/gemini-3.1-flash-lite` |
| Final synthesis | `z-ai/glm-5.3-flash` | `minimax/minimax-m2.7`, then `google/gemini-3.1-flash-lite` |
On a network failure, a model can be retried once. Parse, timeout, context, or structured-output
failures can advance to the next distinct model. If subtopic generation exhausts retryable model
attempts, Y2 can use three deterministic child-query templates; final synthesis has no equivalent
partial-report fallback.
Public profile schemas currently accept `modelConfig.modelId`, but the executed subtopic and
synthesis model order comes from the fixed cascades above. Temperature and maximum output-token
overrides are read; a custom model ID does not replace the cascade's primary model.
Provider requests deny data collection through the configured OpenRouter routing options. See the
platform's privacy and data-processing terms for the complete data-handling boundary.
## Post-processing and delivery
After synthesis, the durable workflow runs these steps in order:
### Generate optional audio
If the profile has Audio Narration enabled, Y2 attempts to create and store the MP3. Audio is
non-critical; a failed audio step does not block the report.
### Enrich the report
Y2 extracts report events and builds the ontology graph and incident metadata used by
downstream product surfaces. Enrichment is required and must finish before delivery.
### Deliver to subscribers
Y2 fans the report out according to each active subscription's email, SMS, combined, or
webhook configuration. Delivery is critical; a terminal delivery failure fails the run.
### Finalize and reschedule
Y2 updates the profile's last-delivery timestamp and hands off the next normal schedule.
Recovery logic repairs the schedule when that final handoff fails.
The compact webhook event contains report state, signal and graph counts, audio availability, and
links to richer API representations. It does not embed the full report body. See
[Webhook Delivery](/docs/delivery/webhooks) for the current contract.
## Failure recovery
Y2 records report-generation state on the profile as `generating`, `completed`, `failed`, or
`canceled`. A run receives a 45-minute lease. The recovery watchdog can renew an in-progress lease
once for 30 minutes before treating an unresolved run as stale.
Terminal failures can be retried up to three times with delays of 5 minutes, 15 minutes, and 1
hour. When workflow history is available, recovery restarts from the last failed durable step;
otherwise it queues a fresh run. After retries are exhausted, Y2 returns to repairing the normal
profile schedule instead of retrying indefinitely.
Duplicate starts are skipped while a valid generation lease or future retry exists. If a newly
activated profile does not immediately produce a second report, an existing run may already be
active or waiting for recovery.
## Configure the implemented workflow
### Define the research target
In **InfoOps → My Profiles**, create or edit a profile. Use a specific topic and custom
instructions that describe the desired decisions, geography, entities, and output emphasis.
### Choose report structure and depth
Set the BLUF sections and review the recursion limitation above. Use the public Profiles API
with recursion disabled when a root-only run is required.
### Set search boundaries
In **Advanced Configuration**, choose search depth, time range, result count, and optional
include or exclude domains.
### Configure delivery and optional outputs
Set the schedule and subscriber method, then enable audio or branding only when the active
workspace has those entitlements.
### Activate and review the first report
Saving a new active profile queues an immediate run. Review its sources, signals, graph, and
delivery history before relying on later scheduled reports.
## API boundary
There is no public endpoint for creating arbitrary workflows or directly starting an internal
workflow ID. Use the Profiles API to create, update, activate, pause, or cancel the configuration;
use the Reports API to read produced reports and derived representations.
The generated [Profiles reference](/docs/api/reference/profiles) is the canonical request schema.
Profile writes require `profiles:write`; report reads require the relevant `reports:*` scope.
## Next steps
Configure the inputs that feed the durable report pipeline.
Understand UTC scheduling, jitter, and schedule recovery.
Configure the optional narration step.
Consume the compact report-generated event safely.
---
# Workspaces (/docs/workspaces)
A workspace holds its own plan, resources, API keys, usage, and memberships. All workspaces use
the same model. Free, Lite, and Pro include one seat; Elite includes five seats, including the owner.
Your first workspace is created during onboarding. Retrying setup or checkout resumes that
workspace. An invited teammate joins the invited workspace without creating another one.
## Upgrade your workspace
Open **Settings → Billing** to manage the active workspace's plan. Upgrading from Pro to Elite
unlocks team seats in that same workspace. Its profiles, reports, keys, projects, and billing
history stay with it. Only the owner can manage billing.
An Elite workspace can have an approved higher seat allowance. Contact Y2 to arrange expansion.
Additional seats do not multiply the workspace's credits, profiles, Automations, or API limits.
## Switch or create workspaces
Use the workspace menu in the app sidebar to switch. Each entry shows its name, plan, and your
role. **Settings → Workspace** also shows seat usage and lets owners and admins rename it.
To create a separate workspace, select **New workspace** and enter a name of 2–80 characters.
It starts on Free with its own resources and billing. Upgrade that workspace to Elite to invite
teammates. Creating it does not copy another workspace's subscription or data.
Switching changes which workspace the app reads and writes. It does not move resources.
Suspended memberships do not appear in the switcher. If no accessible workspace remains, the app
lets you create one before continuing.
## Invite teammates on Elite
1. Switch to the workspace and open **Settings → Members**.
2. An owner or admin creates an invitation for an email address and an `admin`, `member`, or
`viewer` role.
3. The recipient signs in with the matching verified email and requests access.
4. A workspace owner or admin approves the request. The recipient can then use that workspace.
The owner, active members, and unexpired pending invitations must fit within the seat allowance.
An invitation reserves a seat; suspended members do not. Owners and admins can revoke invitations.
Owners can change non-owner roles or remove members. A non-owner can leave the workspace.
## Roles and management permissions
| Operation | Owner | Admin | Member | Viewer |
| --- | :---: | :---: | :---: | :---: |
| Switch into an active seat | Yes | Yes | Yes | Yes |
| View the member roster | Yes | Yes | Yes | Yes |
| Rename the workspace | Yes | Yes | No | No |
| Invite people, approve requests, revoke invites | Yes | Yes | No | No |
| View governance and audit activity | Yes | Yes | No | No |
| Manage workspace API keys | Yes | Yes | No | No |
| Change another member's role or remove them | Yes | No | No | No |
| Manage billing | Yes | No | No | No |
| Delete an empty workspace | Yes | Yes | No | No |
Role requirements also depend on the resource. Owners and admins can manage workspace profiles;
members can write profiles they created; viewers cannot write profiles. Projects and Automation
definitions remain private to their owner. Governance access does not grant access to another
member's private Project content.
## Seat limits and downgrades
When a workspace moves to a one-seat plan, the owner keeps access and other memberships are
suspended. Their membership records remain available for restoration. Returning to Elite restores
members in a deterministic order up to the available seat allowance.
A downgrade applies only to that workspace. Moving to Free or Lite pauses profiles beyond the
new allowance while preserving their ownership and privacy. Resources in other workspaces are
unaffected.
## Governance and shared limits
On Elite, owners and admins can open **Settings → Governance** to review the shared Agent Y2
credit pool, usage by member, API keys, and recent audit records. Credit attribution does not create
separate allowances for each member. Activity units are informational and are not Agent Y2 credits.
The audit log includes membership actions and material Project and Automation changes. It is not
a complete log of every report, chat message, model call, or API operation.
Pro and Elite support workspace API keys. Owners and admins manage them; requests use the
workspace associated with the key. Rotating keys or adding members does not multiply request
limits or credit allowances. See [Plans and limits](/docs/plans) for current allowances.
## Delete a workspace
An owner or admin can delete an empty workspace from **Settings → Workspace** by typing its name.
The checklist identifies billing subscriptions and dependent resources that must be resolved first.
Deletion removes the workspace and its memberships, invitations, audit records, and rate counters.
Users with another accessible membership can continue there.
Account deletion also checks workspace ownership. An account that owns a workspace with other
members must resolve that shared ownership before its billing or account can be removed.
## Related guides
Compare workspace features, seats, API keys, and usage limits
Create and protect workspace credentials
Understand profile ownership and workspace capacity