# Join and Manage the Affiliate Program (/docs/affiliate-program)
Use [**Settings → Referrals**](/app/settings/referrals) to join the Y2 Affiliate Program, copy your
personal link, monitor referral activity, and check payout eligibility. This guide is for paid Y2
customers who own the billing workspace attached to their subscription.
Enroll, copy your link, refresh activity, and review payout eligibility
Compare the Lite, Pro, and Elite plans that qualify a promoter account
Participation is optional. Y2 creates a promoter link only after you accept the current program
terms. The version shown in Settings controls if it differs from this operational summary.
## Before you begin
Your account must meet all three enrollment requirements:
| Requirement | What qualifies |
| --- | --- |
| Verified email | The email in [Account settings](/app/settings/account) has been verified |
| Owned paid workspace | Your account owns a Lite, Pro, or Elite billing workspace |
| Current payment | The owned workspace has an active Stripe subscription that is not expired or canceled |
Admin or member access to another customer's paid organization does not qualify. If billing needs
attention, open [Billing settings](/app/settings/billing) before enrolling.
## Join and create your link
### Open referral settings
Go to [**Settings → Referrals**](/app/settings/referrals). The program card shows whether you
need to verify your email, restore billing, accept the terms, or wait for link activation.
### Review the current terms
Expand **Review the affiliate program terms**. Check the commission rules, review period,
payout requirements, and responsible-promotion rules for the displayed version.
### Accept and join
Select the agreement checkbox, then choose **Join and create my referral link**. Y2 records the
terms version and commission percentage you accepted before starting enrollment.
### Copy the activated link
Link creation runs asynchronously through FirstPromoter. When the complete URL appears, choose
**Copy link** and use that URL in your promotion. If activation is still pending, wait briefly
and use **Refresh referral activity**.
## What happens after enrollment
```mermaid
flowchart LR
enroll["Accept current terms"] --> provision["FirstPromoter link provisioned"]
provision --> visit["New customer opens link"]
visit --> claim["Attribution claimed at workspace creation"]
claim --> payment["Non-zero Lite or Pro payment"]
payment --> review["Commission pending review"]
review --> payout["Approved balance paid when eligible"]
```
A signup, trial, or workspace creation is not a commissionable conversion. Y2 records an eligible
sale only after Stripe confirms qualifying collected revenue.
### Your referral link
Y2 asks FirstPromoter to base a new link token on your lowercase Y2 username. If that token is
already in use, provisioning can append a six-character suffix. Once an established promoter link
is stored, later synchronization keeps it stable rather than renaming it whenever your account is
read.
Always copy the link shown in the referral dashboard. Do not reconstruct or shorten its `via`
token yourself.
### Read the dashboard
After enrollment, the page replaces the join form with your activity dashboard.
| Dashboard area | What it tells you |
| --- | --- |
| Clicks | Provider-reported visits through your referral link |
| Referrals | Referred-customer records attributed to your promoter account |
| Sales | Provider-reported qualifying sales |
| Active customers | Referred customers currently counted as active |
| Payout eligibility | Whether the link, terms, owned plan, and payment requirements are all current |
| Commission activity | Recent amount, currency, review state, and paid state |
| Referral activity | Recent referred-customer state and customer-since date when available |
| Last synced | When Y2 last refreshed activity from FirstPromoter |
The dashboard synchronizes these provider values and shows masked customer email addresses when
available. Use **Refresh referral activity** beside the link to request a new synchronization.
## Preserve attribution
For the strongest chance of correct attribution, the referred customer should:
1. Open the complete referral link before creating a Y2 workspace.
2. Allow marketing/referral tracking in the privacy choices.
3. Complete account and default-workspace setup in the same browser.
4. Purchase an eligible Lite or Pro subscription before attribution expires.
The browser starts FirstPromoter and captures referral attribution only when marketing consent is
enabled. Declining that category, changing browsers, clearing site storage, or removing the link
parameters can prevent the referral from being claimed.
Y2 keeps the first current referral touch it captures in browser storage. When the new customer
creates their default workspace, Y2 claims that attribution for the workspace and sets a 90-day
expiration. A workspace can have only one attribution record; later links do not replace it.
If the attribution is still only **claimed** when it expires, a later payment is rejected for
affiliate tracking. Once a qualifying first payment converts it, later eligible invoices can be
associated with the same attributed subscription.
## What earns commission
The default program offers **15% recurring commission** on eligible net Lite or Pro subscription
revenue actually collected by Y2.
| Commissionable | Not commissionable |
| --- | --- |
| Legitimate new customer attributed through the assigned link | Existing customer, duplicate account, or self-referral |
| Successful, non-zero Lite or Pro payment | Signup, workspace creation, or trial without collected revenue |
| Tax-exclusive cash collected after credits and discounts | Free, Elite, zero-dollar, fully discounted, or test activity |
| Revenue that passes payment, attribution, fraud, and eligibility review | Refunds, chargebacks, disputes, uncollected amounts, or manipulated attribution |
The collected amount is capped at the invoice's tax-exclusive total. Taxes are not part of the
commission basis.
Recurring commission continues only while the attributed Lite or Pro subscription remains active
and paid, the underlying revenue remains eligible, and your own promoter eligibility remains
current.
## Keep your promoter eligibility current
The dashboard evaluates four requirements:
- Referral link activated in the accepted FirstPromoter campaign.
- Current Y2 affiliate terms accepted.
- At least one Lite, Pro, or Elite billing workspace owned by your account.
- An active, current Stripe subscription for an owned eligible workspace.
If the owned billing account becomes Free, trialing, incomplete, canceled, past due, unpaid, or
delinquent, commission and payout eligibility pauses. Access through someone else's organization
does not restore it.
## Review and payout
Commissions may remain pending for at least **60 days** while Y2 reviews payment finality, refunds,
disputes, fraud, attribution, plan eligibility, and promoter eligibility. A pending amount is an
estimate, not an approved payable balance.
Y2 may deny, reverse, offset, or recover an amount that becomes ineligible. The current in-app
terms state a default payout threshold of **$100**. Identity, tax, invoice, or payout-method details
may be required before payment, and the promoter is responsible for taxes on payouts received.
## Troubleshooting
Verify the account email in **Settings → Account**, then reopen **Settings → Referrals**. Y2
cannot create a promoter link until verification is complete.
Confirm that your account owns the billing workspace. Restore an active Lite, Pro, or Elite
Stripe subscription on an owned workspace; membership in another paid organization does not
qualify.
Enrollment is asynchronous. Wait for the provider sync, then use **Refresh referral activity**.
Temporary rate limits, timeouts, and provider outages retry automatically. If the dashboard
displays **Retry**, choose it once the provider is available again.
Follow the message shown on the dashboard. If it asks for support review, contact
[support@y2.dev](mailto:support@y2.dev) and include the error text. Do not create a duplicate
account to work around an email or promoter ownership conflict.
Confirm that the customer used the complete link before workspace creation, allowed marketing
tracking, stayed in the same browser, was new to Y2, and paid a non-zero Lite or Pro invoice
within the attribution window. Signups, trials, Elite purchases, self-referrals, and duplicate
accounts do not qualify.
Describe Y2 truthfully and disclose your material connection near each promotion. Do not
impersonate Y2, spam, stuff cookies, bid on Y2 trademarks, or offer unapproved incentives.
---
# Agent Y2 API (/docs/api/agent-y2)
Agent Y2 exposes the same preconfigured intelligence agent used by the Y2 copilot through two
API-key-authenticated streaming endpoints. It uses the key's workspace, plan entitlements, chat
credit budget, persisted thread history, and allowed Y2 tools.
API callers cannot select an arbitrary model, replace the fixed system instructions, enable
onboarding mode, or attach files in v1. The `model` value on the OpenAI-compatible route is a Y2
routing alias, not model selection.
## Choose an endpoint
Both endpoints use `https://api.y2.dev/api/v1`.
| Endpoint | Stream format | Choose it when |
| --- | --- | --- |
| `POST /agent-y2/chat/stream` | Vercel AI SDK UI message stream | Your client understands the native AI SDK protocol |
| `POST /chat/completions` | OpenAI-style `chat.completion.chunk` server-sent events | Your client already consumes streaming chat completions |
```mermaid
flowchart LR
client["API client"] --> auth["API key + agent:y2"]
auth --> limits["Key, account, and chat-credit checks"]
limits --> context["Workspace thread + entitlements"]
context --> agent["Fixed Agent Y2 model and tools"]
agent --> format{"Response format"}
format --> native["AI SDK UI stream"]
format --> openai["OpenAI-compatible SSE"]
```
The routes share the same Agent Y2-specific rate-limit pool. Switching formats does not create a
second quota.
## Create a scoped key
### Open API Keys
In the workspace that should own the threads and tool actions, open **Settings → Developers →
API Keys**.
### Grant `agent:y2`
Create a dedicated key with the `agent:y2` scope. New API-key creation requires Pro or Elite.
Existing Lite keys remain usable only with the scopes already granted to them.
### Store the key on a trusted server
Send it as `Authorization: Bearer $Y2_API_KEY`. Do not put an Agent Y2 key in browser bundles,
prompts, metadata, logs, or public repositories.
Agent Y2 does not support x402. A `402` response from these routes describes plan, chat-credit, or
upstream-provider credit exhaustion; it is not a payment challenge.
## Send a native request
```bash title="Terminal"
curl -N -i "https://api.y2.dev/api/v1/agent-y2/chat/stream" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"parts": [
{ "type": "text", "text": "What changed in cyber risk this week?" }
]
}
],
"metadata": {
"source": "customer_crm",
"externalThreadId": "case-123"
}
}'
```
The response is an AI SDK UI message stream. API responses omit internal reasoning parts even
though the in-app stream can include them.
Read the `X-Thread-Id` response header and store it. Continue the conversation by sending that ID
in the next native request body:
```json title="request.json"
{
"threadId": "j57...",
"messages": [
{
"role": "user",
"parts": [{ "type": "text", "text": "Which entities should I watch next?" }]
}
]
}
```
## Send an OpenAI-compatible request
```bash title="Terminal"
curl -N -i "https://api.y2.dev/api/v1/chat/completions" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "y2-agent",
"stream": true,
"messages": [
{
"role": "user",
"content": "Summarize the latest Y2 platform capabilities."
}
]
}'
```
The compatibility route requires `stream: true`. `model` is optional; when supplied, it must be
`y2-agent` or `agent-y2`. It emits a role chunk, text-delta chunks, a final chunk with
`finish_reason: "stop"`, and `data: [DONE]`.
To continue a conversation, pass `threadId` in the JSON body or send the prior ID in an
`X-Thread-Id` request header.
System, developer, and tool messages are accepted so OpenAI-oriented clients can serialize their
transcript, but they are excluded from Agent Y2's model context. Extra OpenAI fields such as
`temperature`, `max_tokens`, and tool definitions are not part of the v1 contract and do not
configure the agent.
## Understand thread context
| Request state | Context used by Agent Y2 |
| --- | --- |
| No `threadId` | Creates an API-source thread and uses non-empty user and assistant text from the supplied transcript |
| Existing `threadId` | Verifies the same user, API source, and workspace, then loads up to 40 persisted messages from that thread |
| Latest user message | Saved to the thread and used as the new prompt |
| System, developer, or tool transcript entries | Accepted where compatible but not used to override or extend the fixed prompt |
A thread created in the app cannot be continued through the API. A workspace-scoped API key also
cannot continue a thread from another workspace.
The server returns `400` for an invalid ID shape, `404` for a missing thread, and `403` when the
thread exists outside the caller's allowed source, user, or workspace scope.
## Available tools
The exact tool set is assembled for every request from current workspace entitlements.
| Tool category | Availability |
| --- | --- |
| Y2 documentation search | Always registered |
| Public briefing search and follow | Registered for standard Agent Y2 sessions |
| News search | Registered for standard sessions |
| Web search | Requires the workspace web-search entitlement |
| Profile creation and editing | Requires remaining custom-profile capability and metering context |
| OSINT search | Requires OSINT entitlement |
| Y2 Global Knowledge retrieval | Requires Chat entitlement; searches only authorized shared and key-workspace corpora |
| Cyber, FININT, markets, entity, incident, and investigation tools | Added according to the related intelligence entitlements |
| Image generation | Requires image-generation entitlement and a compatible selected agent path |
Tool availability does not guarantee that the agent will call a tool. Tool actions still enforce
the underlying workspace permissions and resource limits.
Agent Y2 uses its existing `agent:y2` scope when it invokes Y2 Global Knowledge. Do not add
`intel:knowledge` to an Agent Y2 key unless the same integration also calls the direct retrieval
endpoint. Direct retrieval and agent execution are separate least-privilege surfaces.
## Metadata behavior
Both routes accept a strict metadata object:
```json
{
"metadata": {
"source": "ops_console",
"externalUserId": "analyst-42",
"externalThreadId": "case-123"
}
}
```
| Field | Maximum | Current behavior |
| --- | --- | --- |
| `source` | 128 characters | Accepted for compatibility; the thread source is still recorded as `api` |
| `externalUserId` | 256 characters | Accepted but not currently persisted to the thread |
| `externalThreadId` | 256 characters | Stored when Y2 creates a new thread; it does not replace the Y2 `threadId` |
Unknown metadata properties fail strict validation. Keep your own correlation log and never put
secrets or full customer records in metadata.
## Provider privacy controls
The current Agent Y2 executor selects chat models marked as Zero Data Retention capable and sends
OpenRouter provider options with `zdr: true` and `data_collection: "deny"`.
These controls govern the external model request. Y2 still stores API-source threads, messages,
request attribution, tool results, and usage records under Y2's product data lifecycle. See the
[Privacy Policy](/privacy) for the customer-facing data terms.
## Rate limits and response headers
Agent Y2 applies these limits in addition to the API key's normal plan limits:
| Limit scope | Per minute | Per day |
| --- | ---: | ---: |
| API key | 5 | 100 |
| User or workspace aggregate | 10 | 250 |
Inspect `Retry-After`, the normal `X-RateLimit-*` headers, and the
`X-Y2-Agent-RateLimit-*` headers. Successful streams also return `X-Thread-Id`, `X-Y2-Agent: y2`,
and `X-Y2-Agent-Mode: copilot`.
## Error guide
| Status | Common cause | Response |
| --- | --- | --- |
| `400` | Invalid JSON, missing latest user text, unsupported alias, invalid thread ID, or native `modelId`, `isOnboarding`, or `attachments` | Correct the request; do not retry unchanged |
| `401` | Missing, malformed, revoked, or invalid API key | Replace or rotate the credential |
| `402` | Subscription, monthly chat-credit, or upstream provider credit exhaustion | Check plan and chat usage; do not start x402 handling |
| `403` | Missing scope, no chat entitlement, blocked access, or unauthorized thread | Check key scope, workspace, plan, and thread origin |
| `404` | Supplied thread no longer exists | Start a new thread or correct the stored ID |
| `429` | Normal API or shared Agent Y2 limit exceeded | Back off for `Retry-After` seconds |
| `500` | Stream preparation or provider execution failed | Retry with backoff and retain `X-Request-Id` for support |
## Related reference
Verify the exact request schemas, response headers, and generated examples
Understand key format, scopes, tenant resolution, and plan limits
Connect an MCP-compatible agent to Y2's exposed tools
---
# AI Quickstart (/docs/api/agentic-dx)
Use this page before asking a coding agent to build against Y2. It gives the agent stable discovery
URLs, separates the v1 and v2 API roots, identifies the required scopes, and keeps credentials out
of the prompt.
The button copies a short bootstrap prompt that tells the agent to read this page and the MCP guide.
It intentionally links to current documentation instead of embedding a second copy of plan limits
that can drift.
## Agent build loop
```mermaid
flowchart LR
discover["Discover\nllms.txt"] --> context["Load only relevant docs"]
context --> contract["Read OpenAPI operation"]
contract --> access["Choose tenant + scopes"]
access --> implement["Implement server-side"]
implement --> verify["Smoke test + inspect headers"]
```
### Discover the documentation
Start with `https://y2.dev/llms.txt`. It is the compact index generated from the documentation
tree. Load `llms-full.txt` only when the task needs broad cross-product context.
### Read the relevant guide and operation
Use the manual guide for intent and workflow, then use the generated endpoint reference or raw
OpenAPI operation for the exact path, method, parameters, scopes, schemas, x402 setting, and
response headers.
### Resolve authentication and tenancy
Create the API key in the workspace that should own the request. Grant only the documented
scopes. Let Y2 derive user and workspace authorization from the key; never treat a caller-
supplied user or workspace ID as proof of access.
### Implement from the contract
Keep `Y2_API_KEY` in a server or local agent environment. Generate types from OpenAPI when
useful, but preserve Y2 response headers and structured errors in the integration layer.
### Verify one real request
Run a bounded curl request or use the API Workbench. Confirm the status, response shape,
`X-Request-Id`, API version, pagination, and rate-limit headers before expanding the workflow.
## Canonical context URLs
| Resource | URL | Use |
| --- | --- | --- |
| Documentation index | `https://y2.dev/llms.txt` | Discover pages without loading the full corpus |
| Full documentation | `https://y2.dev/llms-full.txt` | Broad context for cross-cutting work |
| Individual raw page | `https://y2.dev/docs/{path}.mdx` | Load one page as Markdown |
| API guide | `https://y2.dev/docs/api` | Understand surfaces, auth choices, and workflow boundaries |
| Generated reference | `https://y2.dev/docs/api/reference` | Inspect operation-specific schemas and examples |
| OpenAPI 3.1 document | `https://y2.dev/api/openapi.yaml` | Generate clients and validate exact contracts |
| Authentication guide | `https://y2.dev/docs/api/authentication` | Choose key scopes and understand tenant resolution |
| Agent Y2 guide | `https://y2.dev/docs/api/agent-y2` | Integrate native or OpenAI-compatible agent streams |
| MCP guide | `https://y2.dev/docs/api/mcp` | Configure the separately distributed local MCP server |
`llms.txt`, `llms-full.txt`, and raw `.mdx` pages are generated from `content/docs` during the site
build and served by the local Vite documentation middleware during development.
Manual guides explain goals and safe usage. If a hand-written example conflicts with a generated
operation, use `public/api/openapi.yaml` and the generated endpoint page as the field-level source
of truth, then report the stale guide.
## Base URLs
| Surface | Base URL | Examples |
| --- | --- | --- |
| Core v1 | `https://api.y2.dev/api/v1` | Reports, profiles, news, OSINT, webhooks, subscriptions, Agent Y2 |
| Intelligence v2 | `https://api.y2.dev/api/v2` | Changes, incidents, entities, graphs, markets, FININT, signals, cyber |
The OpenAPI document declares an operation-level `https://api.y2.dev` server for v2 paths because
those paths already include `/api/v2`. Do not combine the v1 base with a v2 operation path.
## API-key plan and scope map
This table describes the new API-key path. An endpoint that explicitly supports x402 can also be
called without an API key by completing that endpoint's payment flow.
| Surface | New key minimum | Required scope |
| --- | --- | --- |
| Reports | Pro | `reports:read` |
| Report audio | Pro | `reports:audio` |
| Profiles | Pro | `profiles:read` or `profiles:write` by operation |
| News | Pro | `news:read` |
| OSINT v1 | Pro | `osint:read` |
| FININT and markets v2 | Pro | `intel:finint` |
| Cyber v2 | Pro | `intel:cyber` |
| Explorer entities, incidents, and graphs | Pro | `intel:explorer` |
| Webhook management API | Pro | `webhooks:manage` |
| Agent Y2 stream | Pro | `agent:y2` |
| MCP docs and OpenAPI resources | None | None |
| MCP data or Agent Y2 tools | Pro for a new key | Scope used by each underlying API operation |
Lite includes in-app FININT, Cyber, Explorer, full Situation Room access, and webhook delivery, but
cannot create new API keys. A Lite workspace with existing keys retains those credentials and their
historical limit of 10 requests per minute and 500 per day. This grace does not grant new scopes or
new key creation.
| Plan | New keys | Default per-key request limits |
| --- | ---: | ---: |
| Free | 0 | No key access |
| Lite | 0 | Existing keys only: 10/minute and 500/day |
| Pro | 5 | 30/minute and 5,000/day |
| Elite | 25 | 120/minute and 50,000/day |
Elite organization workspaces can include up to five seats. Workspace members share tenant
resources and account-level rate-limit aggregates; creating more keys does not multiply the
workspace aggregate quota.
## Give an agent safe instructions
Use this compact task template after the bootstrap prompt has loaded the docs:
```text title="Agent task template"
Implement [specific Y2 workflow] in this codebase.
Before editing:
1. Read the relevant Y2 manual guide.
2. Read the exact operation in https://y2.dev/api/openapi.yaml.
3. Confirm the base URL, required scope, pagination, x402 support, and error responses.
Constraints:
- Read Y2_API_KEY from the server or MCP client environment only.
- Never put the key in source, browser code, prompts, tool arguments, or logs.
- Let the API key determine tenant access.
- Use the narrowest scopes and bounded page sizes.
- Handle 401, 402, 403, 404, 409/412 where documented, 429, and 5xx.
- Preserve X-Request-Id and Retry-After for diagnostics.
Verify with one bounded request and report the observed status and response shape.
```
## MCP boundary
The platform repository defines the HTTP APIs, OpenAPI contract, docs, billing rules, and Agent Y2
executor. `@y2-intel/mcp` is documented as a separate stdio package and its implementation is not
present in this repository.
Follow the [MCP guide](/docs/api/mcp) for the intended configuration. Before automating against a
specific package release, inspect its actual `tools/list`, `resources/list`, and `prompts/list`
responses instead of assuming every documented capability exists in the installed version.
When configuring a local MCP client, pass `Y2_API_KEY` through the client environment. Grant
`agent:y2` only when the agent should be able to use entitled account actions; documentation and
OpenAPI discovery do not require that scope.
## Smoke tests
```bash title="Terminal"
curl -sS -D - "https://api.y2.dev/api/v1/reports?limit=1" \
-H "Authorization: Bearer $Y2_API_KEY"
```
```bash title="Terminal"
curl -sS -D - "https://api.y2.dev/api/v2/finint?limit=1" \
-H "Authorization: Bearer $Y2_API_KEY"
```
Use a key that has the matching read scope. `-D -` prints the response headers so the agent can
verify request IDs, API version, and remaining quota instead of checking only the JSON body.
## Related guides
Choose the right API surface and authentication path
Understand key format, scopes, IP rules, limits, and workspace tenancy
Generate clients and maintain the contract
Run a live request and copy environment-based snippets
---
# Authentication (/docs/api/authentication)
Y2 authenticates most API requests with a workspace-bound bearer key. Endpoints that explicitly
support [x402](/docs/api/x402) also offer a separate paid request path when no bearer key is sent.
## Authentication flow
```mermaid
flowchart LR
request["HTTP request"] --> bearer{"Authorization starts\nwith Bearer?"}
bearer -->|Yes| key["Validate hash, state, plan, IP"]
key --> scope["Check operation scope"]
scope --> limits["Increment key + tenant limits"]
limits --> handler["Run in key workspace"]
bearer -->|No| payment{"Endpoint supports x402?"}
payment -->|Yes| x402["Payment challenge or signed retry"]
payment -->|No| unauthorized["401"]
```
## Plan requirements
| Plan | New keys | Default limit per key | Notes |
| --- | ---: | ---: | --- |
| Free | 0 | No key access | Use in-app features or supported x402 endpoints |
| Lite | 0 | Existing keys: 10/minute and 500/day | Grandfathered keys only; historical active-key capacity is 2 |
| Pro | 5 | 30/minute and 5,000/day | New API-key access |
| Elite | 25 | 120/minute and 50,000/day | New API-key access plus team governance |
Existing keys that were already eligible under the Lite grace policy remain visible, usable,
rotatable, and revocable. A later downgrade from Pro or Elite marks keys plan-ineligible; it does
not create new Lite grace access.
Rate limits apply per key and again to the account aggregate: the workspace for workspace-bound
keys, or the user for a legacy unscoped key. Creating more keys does not multiply the tenant quota.
## Create a key
### Select the correct workspace
Use the workspace switcher first. Y2 binds the new key to the active workspace, and later API
requests resolve their tenant from that stored binding.
### Open API Keys
Go to [**Developers → API Keys**](/app/developers/api-keys). Creating a key requires an owner or
admin role in the workspace and a Pro or Elite plan.
### Name and scope the key
Use a unique operational name such as `report-exporter-prod`. Select only the scopes required
by that integration. At least one scope is required.
### Add exact IPs when useful
Optionally add the stable public egress IP addresses that are allowed to use the key. Confirm
the deployed service's actual egress address before enabling this restriction.
### Copy the secret once
Y2 displays the full key only in the creation result. Store it in a secret manager or protected
environment variable before closing the dialog.
## Key format and storage
Y2 generates 32 random bytes and encodes them as 64 hexadecimal characters after the `y2_`
prefix:
```text
y2_{64 lowercase hexadecimal characters}
```
Only the SHA-256 hash and a short display prefix are stored. The dashboard cannot retrieve the
plaintext later. Regeneration creates new secret material and immediately invalidates the prior
value.
## Send the key
```bash title="Terminal"
curl -sS -D - "https://api.y2.dev/api/v1/reports?limit=1" \
-H "Authorization: Bearer $Y2_API_KEY"
```
The authentication scheme is the case-sensitive `Bearer ` prefix followed by the key. Keep the
credential server-side; never place it in a browser bundle, query string, prompt, tool argument, or
log message.
[API Workbench](/app/developers/api-workbench) keeps the pasted value in React component state and
sends it only when you run a request. Generated curl, TypeScript, and Python snippets continue to
reference `Y2_API_KEY`. Treat the browser session and screen as sensitive while the key is
present.
## Workspace scope
| Boundary | Current behavior |
| --- | --- |
| Resource access | Profiles, subscriptions, reports, Projects, Automations, webhooks, threads, public IDs, and usage are resolved in the key's stored workspace where the operation is tenant-scoped |
| Key management | Workspace owners and admins can list and manage workspace keys; other roles receive no key list |
| Aggregate limits | All keys in a workspace contribute to the same plan-level minute and daily pool |
| Workspace switching | Switching the application workspace does not move an existing key; it changes where a newly created key is bound |
Do not accept a user-provided `workspaceId` as authorization. If an operation accepts a resource ID,
the backend still verifies that the resource belongs to the tenant resolved from the key.
## Scopes
| Scope | Grants access to |
| --- | --- |
| `reports:read` | Report lists, individual reports, text, signals, and report graphs where specified |
| `reports:audio` | Report audio and audio-text operations |
| `profiles:read` | Listing profile subscription records in the key workspace |
| `profiles:write` | Creating and changing owned profiles, including destructive profile deletion |
| `projects:read` | Listing and inspecting owned Projects in the key workspace |
| `projects:write` | Creating, changing, archiving, restoring, and pinning owned Projects |
| `automations:read` | Listing owned Automation definitions and run history |
| `automations:write` | Creating, changing, archiving, and manually running owned Automations |
| `news:read` | News items, recaps, and feed discovery |
| `webhooks:manage` | Webhook configuration and subscription-delivery webhook operations |
| `osint:read` | v1 Situation Room endpoints and the v2 change feed |
| `intel:finint` | v2 FININT, markets, shared incidents, and shared signals |
| `intel:cyber` | v2 cyber graph, CVEs, threat actors, shared incidents, and shared signals |
| `intel:explorer` | v2 entities, entity graphs, shared incidents, and shared signals |
| `intel:knowledge` | Direct bounded retrieval across authorized Y2 Global Knowledge corpora |
| `agent:y2` | Native and OpenAI-compatible Agent Y2 streams with entitled account tools |
An array of required Intel scopes in the generated reference means **any one** of the listed scopes
can authorize that shared endpoint. Always check the exact operation's `x-required-scopes` value.
Plan entitlements are enforced again during key validation. Storing a scope on a key does not keep
that capability active after the workspace loses the required plan feature.
`intel:knowledge` and `agent:y2` are intentionally separate. Direct
`POST /api/v2/intel/knowledge/retrieve` calls require `intel:knowledge`. Agent Y2 routes continue to
require only `agent:y2`; an entitled agent can invoke Global Knowledge through the shared internal
tool. Existing keys are never expanded automatically—edit or rotate a key deliberately when an
integration needs `intel:knowledge`, `projects:*`, or `automations:*`.
`profiles:write`, `projects:write`, `automations:write`, `webhooks:manage`, and `agent:y2` are not
read-only. Isolate them from reporting keys and grant them only to integrations designed to
perform the related actions.
## IP allowlisting
The dashboard's IP restriction currently performs an exact string comparison against the client IP
resolved from `CF-Connecting-IP`, the first `X-Forwarded-For` value, or `X-Real-IP`.
- Enter individual IPv4 or IPv6 addresses only.
- CIDR ranges are not parsed by the current middleware.
- For hosted jobs, allow the service's public egress IP, not a private container address.
- Removing every entry disables the key-specific IP restriction.
- A resolved address not present in the list returns `403` with `IP_NOT_ALLOWED`.
Core API responses use permissive CORS headers, and stored `allowedOrigins` metadata is not
currently enforced by request authentication. Use exact IP restrictions where appropriate and
keep keys out of client-side applications.
## x402 selection
On an x402-enabled operation, the request path is selected by the header prefix:
| Request | Result |
| --- | --- |
| Valid `Authorization: Bearer ...` | Validate key, scope, IP, and key/account limits |
| Invalid, expired, revoked, or unauthorized bearer key | Return the API-key `401` or `403`; do not fall back to payment |
| No header beginning with `Bearer ` | Enter x402 on a supported endpoint, or return an authentication error when x402 is unavailable |
| Signed x402 retry | Verify replay protection, wallet and endpoint limits, and settlement before returning data |
Do not send a placeholder bearer header when you intend to use x402. See the
[x402 guide](/docs/api/x402) and the operation's generated reference for price, network, and
payment headers.
## Rate-limit headers
Y2 uses fixed UTC minute and day windows. Authenticated responses expose both pools:
| Header family | Meaning |
| --- | --- |
| `X-RateLimit-Limit-Minute`, `X-RateLimit-Remaining-Minute` | Per-key minute pool |
| `X-RateLimit-Limit-Day`, `X-RateLimit-Remaining-Day` | Per-key daily pool |
| `X-RateLimit-Limit-User-*`, `X-RateLimit-Remaining-User-*` | Account aggregate; legacy header name, workspace-backed for workspace keys |
| `X-RateLimit-Reset-Minute`, `X-RateLimit-Reset-Day` | Unix reset timestamps when included |
| `Retry-After` | Seconds to wait after a `429` |
Agent Y2 adds a third, endpoint-specific header family. See
[Agent Y2 rate limits](/docs/api/agent-y2#rate-limits-and-response-headers).
## Rotate, revoke, or delete
Replaces the stored hash, resets key usage metadata, reactivates the record, and displays a new
secret once. The old secret stops working immediately.
Deactivates the key without deleting its record. Repeating the action is safe.
Permanently removes the key record. Use this only after dependent integrations have been
migrated.
For routine rotation, deploy support for the replacement secret before invalidating the old key if
your architecture allows parallel credentials. The dashboard's **Regenerate** action does not
provide an overlap window for the same key record.
## Errors and lockout
Y2 API errors use RFC 9457-style problem details with a compatibility `error` object:
```json
{
"type": "https://api.y2.dev/problems/insufficient-scope",
"title": "Forbidden",
"status": 403,
"detail": "Insufficient permissions. Requires reports:read scope",
"instance": "urn:y2:request:req_...",
"code": "INSUFFICIENT_SCOPE",
"requestId": "req_...",
"error": {
"message": "Insufficient permissions. Requires reports:read scope",
"code": "INSUFFICIENT_SCOPE"
}
}
```
| Status | Common authentication cause |
| --- | --- |
| `401` | Missing header, unknown key, revoked key, or expired key |
| `403` | Plan no longer eligible, missing operation scope, or IP not allowed |
| `429` | Per-key/account quota exceeded or source IP temporarily locked after repeated invalid credentials |
Five invalid or unknown key attempts from one resolved IP within 15 minutes trigger an authentication
lockout for that rolling window. Revoked, expired, and plan-ineligible known keys do not count as
brute-force failures. If a valid key suddenly receives a pre-validation `429`, stop retrying and
wait for the lockout window to clear.
## Related reference
Check the exact scope, x402 setting, schemas, and response headers
Compare key counts, plan features, and workspace limits
Run a bounded authenticated request without copying the secret into snippets
---
# Automations API (/docs/api/automations)
Automations run bounded Agent Y2 instructions on a schedule or after a selected profile report.
Definitions and runs remain owner-private inside the API key workspace.
## Scopes and availability
| Operation | Scope |
| --- | --- |
| List definitions, inspect one, or read run history | `automations:read` |
| Create, update, archive, or request a manual run | `automations:write` |
Automations and both API scopes require Pro or Elite. The same workspace definition limits,
entitlement checks, overlap policy, and Chat credit admission used by the application also apply to
API requests.
## Create a scheduled Automation
```bash
curl -sS "https://api.y2.dev/api/v1/automations" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: automation-daily-watch-2026-07" \
-d '{
"name": "Daily supply chain watch",
"instructions": "Summarize material changes and cite the strongest evidence.",
"projectId": "prj_0123456789abcdef01234567",
"trigger": {
"kind": "schedule",
"frequency": "daily",
"timeOfDay": "13:00",
"timezone": "America/Chicago"
},
"notificationPolicy": "failures_only",
"status": "active"
}'
```
For a report-completion trigger, use:
```json
{
"kind": "profile_report_completed",
"profileId": "prf_0123456789abcdef01234567"
}
```
## Inspect and update
Use `GET /api/v1/automations`, `GET /api/v1/automations/{automationId}`, and
`GET /api/v1/automations/{automationId}/runs`. List responses are bounded to 50 rows per request.
Definition and run-history collections return the standard opaque `nextCursor` and `links.next`
fields for complete traversal.
`PATCH /api/v1/automations/{automationId}` accepts definition fields or a status-only lifecycle
change. An archived Automation cannot be edited or resumed. Send the latest `ETag` in `If-Match`
when coordinating multiple writers.
## Request a manual run
```bash
curl -sS -X POST "https://api.y2.dev/api/v1/automations/$AUTOMATION_ID/runs" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Idempotency-Key: manual-run-2026-07-24T1500Z"
```
The endpoint returns `202 Accepted`. The idempotency key is required and prevents duplicate
admission for retries. Poll the run-history endpoint for queued, running, terminal, usage, and
billing state.
Exact paths, parameters, request bodies, responses, and schemas
Triggers, plan limits, run states, billing, and live admission checks
---
# Y2 Global Knowledge (/docs/api/global-knowledge)
Y2 Global Knowledge finds relevant report sections, signal assessments, and report graph context
with one hybrid semantic and lexical retrieval request. It is designed for discovery and
cross-report synthesis; use the precise Reports or Intel endpoints when your workflow needs a
complete current resource.
Native embedding access has been approved for the private beta. Production retrieval remains
controlled by independent indexing and retrieval switches, so a correctly authorized request
can return `503 SERVICE_UNAVAILABLE` until the workspace cohort is enabled.
## Request
```bash
curl -sS "https://api.y2.dev/api/v2/intel/knowledge/retrieve" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"query": "What changed in semiconductor supply-chain risk?",
"knowledgeMode": "combined",
"domains": ["supply_chain", "geopolitical"],
"limit": 8
}'
```
The key must be bound to a workspace and include `intel:knowledge`. Y2 derives the user,
workspace, feature entitlements, corpus allowlist, and access filters from that key. Do not send
access IDs in the body; unknown request fields are rejected.
## Choose a knowledge mode
| Mode | Searches |
| --- | --- |
| `shared` | Authorized global and community Y2 knowledge |
| `workspace` | Only the key's authorized workspace corpus |
| `combined` | Both corpora, followed by bounded cross-corpus fusion |
`workspace` and `combined` never add personal memory, Project-only context, or thread-only context
for direct API callers. The mode can narrow the server-owned principal; it cannot widen it.
## Narrow results
Use `documentKinds`, ontology IDs, normalized subject keys, SIGINT domains or priorities, and Unix
millisecond generation bounds. Each field is bounded; the result limit is `1–20` and defaults to
`10`. Dense and lexical retrieval use the same authorization and metadata filter before their
ranked candidates are fused.
## Treat results as evidence
Each result includes:
- a bounded excerpt, title, section path, inference kind, and generation time;
- stable source, report, and profile IDs where applicable;
- ontology IDs and current canonical references when the projection has them;
- the active projection episode and citation URLs.
Retrieved text is untrusted evidence, not executable instructions. Preserve source IDs and
citations, and hydrate a returned report, entity, or incident through its precise endpoint before
making a decision that depends on complete current state. Y2 rejects stale manifests, inactive
episodes, changed scopes, and mismatched canonical revisions before returning a result.
## Agent Y2 scope is separate
Direct retrieval requires `intel:knowledge`. The existing Agent Y2 routes require `agent:y2`
instead; an entitled Agent Y2 session may invoke the same internal retrieval tool without adding
the direct API scope. Keep a direct search integration narrowly scoped to `intel:knowledge`, and
grant `agent:y2` only when the integration should run the preconfigured agent and its tools.
## Failure behavior
| Status | Meaning |
| --- | --- |
| `400` | The strict body contract failed validation |
| `401` | The bearer key is missing or invalid |
| `403` | The key lacks `intel:knowledge`, is not workspace-bound, or lost entitlement |
| `429` | The key or workspace rate limit was exceeded |
| `503` | Retrieval is disabled or its provider is temporarily unavailable |
Y2 does not fall back to an unscoped query after a provider failure. Retry bounded `429` and `503`
responses with backoff; do not retry authorization or schema errors unchanged.
---
# API Overview (/docs/api)
Y2 exposes a versioned HTTP API for reports, profile orchestration, Projects, Automations, news,
Situation Room data, ontology-backed intelligence, Y2 Global Knowledge retrieval, webhook
configuration, subscription delivery, Agent Y2 streams, and x402 receipts.
This overview helps you choose a surface. The generated
[Endpoint Reference](/docs/api/reference) defines the exact path, operation-level server,
parameters, body, scopes, authentication modes, media types, schemas, and response headers.
## Request path
```mermaid
flowchart LR
client["Integration"] --> surface{"API surface"}
surface --> v1["Core v1"]
surface --> v2["Intel v2"]
v1 --> auth{"Bearer key or x402"}
v2 --> auth
auth --> tenant["Scope + tenant + limits"]
tenant --> operation["OpenAPI operation"]
operation --> response["Resource, collection, representation, or stream"]
```
## Base URLs
| Surface | Base URL | Contains |
| --- | --- | --- |
| Core v1 | `https://api.y2.dev/api/v1` | Agent Y2, reports, profiles, Projects, Automations, news, OSINT, webhooks, subscriptions, receipts |
| Intelligence v2 | `https://api.y2.dev/api/v2` | Change feed, incidents, entities, graphs, markets, FININT, signals, cyber |
The OpenAPI document defines core operation paths relative to `/api/v1`. Its v2 operation paths
already contain `/api/v2` and override the server to `https://api.y2.dev`. A generator that honors
operation-level servers produces the correct URL; do not concatenate a v2 path onto the v1 base.
## Choose authentication
| Path | Use when | Identity and limits |
| --- | --- | --- |
| Bearer API key | The integration should access a Y2 workspace repeatedly | Key scopes, key IP restriction, per-key quota, and workspace/user aggregate quota |
| x402 | A listed read operation should be paid per request without provisioning a key | Payment signature, replay protection, wallet quota, endpoint ceiling, and settlement |
| Public receipt lookup | A client needs the status of an x402 settlement nonce | No API key; receipt lookup only |
New keys require Pro or Elite. Lite cannot create keys, though keys already covered by the Lite
grace policy continue at 10 requests per minute and 500 per day. x402 support is independent and is
declared one operation at a time.
See [Authentication](/docs/api/authentication) for workspace binding and scopes, or
[x402 Payments](/docs/api/x402) for the payment handshake.
## Choose a surface
Stream the preconfigured workspace-aware agent through native or OpenAI-compatible SSE
List and retrieve generated reports, text, signals, graphs, audio, and audio text
List subscription records and create, replace, patch, or delete owned profiles
Create and manage owner-private intelligence workspaces
Manage durable Agent Y2 definitions, manual runs, and run history
Read direct-source news items, AI recaps, and the current feed catalog
Read v1 Situation Room events, map data, country resources, transport, markets, and source health
Consume ontology-backed changes, incidents, entities, graphs, FININT, signals, and cyber data
Retrieve bounded, authorized evidence across Y2 reports and signal assessments
Create, inspect, update, test, and delete webhook configurations
Change the supported delivery configuration for a profile subscription
Look up an x402 receipt by nonce
## Core v1 route map
| Area | Routes | Bearer scope | x402 |
| --- | --- | --- | --- |
| Agent Y2 | `POST /agent-y2/chat/stream`, `POST /chat/completions` | `agent:y2` | No |
| Reports | `GET /reports`, `GET /reports/{reportId}` and report subresources | `reports:read`; audio uses `reports:audio` | Yes where declared |
| Profiles | `GET`, `POST /profiles`; `PUT`, `PATCH`, `DELETE /profiles/{profileId}` | Read or write scope by operation | No |
| Projects | `GET`, `POST /projects`; `GET`, `PATCH /projects/{projectId}` | `projects:read` or `projects:write` | No |
| Automations | `GET`, `POST /automations`; `GET`, `PATCH /automations/{automationId}`; `GET`, `POST /automations/{automationId}/runs` | `automations:read` or `automations:write` | No |
| News | `GET /news`, `/news/recaps`, `/news/feeds` | `news:read` | Yes |
| OSINT | `GET /osint/*` including country resources | `osint:read` | Yes |
| Webhooks | `/webhooks`, `/webhooks/{webhookId}`, `/test` | `webhooks:manage` | No |
| Subscription delivery | `PATCH /subscriptions/{subscriptionId}/delivery` | `webhooks:manage` | No |
| x402 receipt | `GET /x402/receipts/{nonce}` | None | Public lookup |
The reports list is workspace-contextual for bearer callers. In particular,
`GET /profiles` lists profile subscription records available to the key workspace; it is not a
public community-catalog search endpoint.
## Intel v2 route map
| Route | Bearer scope |
| --- | --- |
| `GET /changes` | `osint:read` |
| `GET /incidents`, `GET /incidents/{incidentId}` | Any of `intel:explorer`, `intel:finint`, or `intel:cyber` |
| `GET /entities`, `/entities/{entityId}`, `/entities/{entityId}/graph` | `intel:explorer` |
| `GET /markets`, `/finint` | `intel:finint` |
| `GET /signals` | Any of `intel:explorer`, `intel:finint`, or `intel:cyber` |
| `GET /cyber/graph`, `/cyber/cves`, `/cyber/actors` | `intel:cyber` |
| `POST /intel/knowledge/retrieve` | `intel:knowledge` |
These paths are relative to `https://api.y2.dev/api/v2` in the table above. Every current v2
operation except Y2 Global Knowledge also declares x402 metadata in OpenAPI. Global Knowledge is
API-key-only because its authorization requires a Y2 workspace principal.
## Success representations
Do not assume every successful endpoint returns the same JSON envelope.
| Contract kind | Typical response |
| --- | --- |
| Collection | `{ "data": [...], "meta": {...}, "links": {...} }` or operation-specific pagination fields |
| Resource | `{ "data": {...} }` plus operation-specific fields and headers |
| Representation | Plain text, audio metadata, or an optional redirect as documented |
| Agent stream | `text/event-stream` in AI SDK UI or OpenAI-compatible chunk format |
| Delete | `204 No Content` for the documented destructive operations |
Collection cursors are opaque. Persist the returned next cursor or watermark and send it back
unchanged; do not parse it or construct one from record IDs.
## Errors
JSON failures use `application/problem+json` with stable top-level fields and a v1 compatibility
object:
```json
{
"type": "https://api.y2.dev/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid API key",
"instance": "urn:y2:request:req_...",
"code": "INVALID_API_KEY",
"requestId": "req_...",
"error": {
"message": "Invalid API key",
"code": "INVALID_API_KEY"
}
}
```
| Status | Common meaning |
| --- | --- |
| `400` | Invalid query, ID, cursor, JSON, or request body |
| `401` | Missing or invalid bearer authentication |
| `402` | x402 challenge or rejection; on Agent Y2, plan or credit exhaustion instead |
| `403` | Missing scope, ineligible plan, IP restriction, or tenant authorization failure |
| `404` | Resource or thread not found in the allowed scope |
| `409` | Idempotency or resource-state conflict |
| `412` | `If-Match` does not match the current representation |
| `415` / `422` | Unsupported representation or operation-specific semantic failure |
| `429` | Key, account, endpoint, wallet, or abuse-control limit exceeded |
| `500` / `503` | Internal failure, unavailable dependency, or unavailable data path |
Retain `X-Request-Id` and the problem `code` in integration logs. Follow `Retry-After` only when the
response indicates a retryable limit or availability failure.
## Version and diagnostic headers
| Header | Meaning |
| --- | --- |
| `X-Y2-API-Version` | `1.0` for v1 operations and `2.0` for v2 operations |
| `X-Y2-Schema-Version` | Shared response-schema version, currently `1.1` |
| `X-Request-Id` | Per-request diagnostic identifier |
| `ETag` | Strong validator on operations that support conditional writes |
| `Location` | Canonical URL after resource creation |
| `Idempotency-Key` | Echoed retry key on operations that support idempotent creation |
| `X-Y2-Next-Cursor`, `X-Y2-As-Of`, `X-Y2-Watermark` | Collection and change-feed checkpoints where applicable |
## Explore and verify
### Read the task guide
Start with [Integration Recipes](/docs/api/integration-recipes) or the manual guide for the
product area.
### Open the generated operation
Verify method, server, scope, x402 metadata, parameters, request body, and responses.
### Run a bounded request
Use [API Workbench](/app/developers/api-workbench) or curl with the smallest useful `limit`.
### Capture the contract evidence
Check the body and diagnostic headers before implementing pagination, retries, or mutations.
## Integration guides
Ground a coding agent in Y2 docs and OpenAPI before it writes code
Download the contract and generate a client
Build and run requests from the current OpenAPI document
Configure the separately distributed local MCP server
---
# Integration Recipes (/docs/api/integration-recipes)
Use these recipes when you already have a Y2 API key and need a production-shaped integration.
They use the canonical schemas in the [OpenAPI document](/api/openapi.yaml). Keep
`Y2_API_KEY` and webhook secrets in server-side environment variables.
```mermaid
flowchart LR
A{What are you building?}
A -->|Grounded answer| B[Report metadata + Markdown]
A -->|Map or spatial analysis| C[GeoJSON FeatureCollection]
A -->|Analytics warehouse| D[Intel v2 JSON pages]
A -->|Continuously updated system| E[Change feed + webhooks]
```
Persist public IDs such as `rpt_...` and `prf_...`. Legacy Convex document IDs are not a
stable integration contract.
## Ground an agent with a report and its sources
Fetch the report's structured source metadata and canonical Markdown representation in parallel.
Retry only rate-limit and server failures, and retain `requestId` when logging a Problem Details
response.
```ts
const authorization = `Bearer ${process.env.Y2_API_KEY}`;
const reportId = "rpt_0123456789abcdef01234567";
const reportUrl = `https://api.y2.dev/api/v1/reports/${reportId}`;
async function requireSuccess(response: Response) {
if (response.ok) return response;
const problem = await response.json();
const error = new Error(
`${problem.code}: ${problem.detail} (request ${problem.requestId})`,
);
Object.assign(error, {
retryable: problem.status === 429 || problem.status >= 500,
problem,
});
throw error;
}
const [metadataResponse, markdownResponse] = await Promise.all([
fetch(`${reportUrl}?include=sources&view=agent`, {
headers: { Authorization: authorization },
}),
fetch(reportUrl, {
headers: { Authorization: authorization, Accept: "text/markdown" },
}),
]);
const [metadata, markdown] = await Promise.all([
requireSuccess(metadataResponse).then((response) => response.json()),
requireSuccess(markdownResponse).then((response) => response.text()),
]);
answerWithCitations(
markdown,
metadata.data.sources.map((source: { url: string }) => source.url),
);
```
The repository checks its generated TypeScript definitions with:
```bash
bun run generate:api-types
bun run check:api-types
```
External TypeScript projects can generate their own types from
`https://y2.dev/api/openapi.yaml` with an OpenAPI 3.1-compatible generator.
## Import regional intelligence into a GIS
Request GeoJSON from the regional OSINT endpoint. Coordinates use WGS 84 longitude-first order:
`[longitude, latitude]`.
```python
import os
import requests
response = requests.get(
"https://api.y2.dev/api/v1/osint/regional",
headers={
"Authorization": f"Bearer {os.environ['Y2_API_KEY']}",
"Accept": "application/geo+json",
},
params={
"bbox": "-122.5,32.5,-96.8,49.2",
"datetime": "2026-07-01T00:00:00Z/2026-07-21T23:59:59Z",
"limit": 200,
},
timeout=30,
)
response.raise_for_status()
feature_collection = response.json()
assert feature_collection["type"] == "FeatureCollection"
assert all(
feature["type"] == "Feature"
for feature in feature_collection["features"]
)
next_page = next(
(
link["href"]
for link in feature_collection.get("links", [])
if link.get("rel") == "next"
),
None,
)
```
The bounding box filter excludes records without matching coordinates. Open the returned
FeatureCollection directly in QGIS or ArcGIS, or load it with GeoPandas. Follow the item in
`links` whose `rel` is `next` to retrieve another page.
## Sync financial intelligence into a warehouse
The semantic `/api/v2/finint` collection returns JSON with pagination in `meta.page` and
`links.next`. Merge on the canonical public `id`, and treat the returned next link as an opaque,
filter-bound continuation.
```python
import os
from urllib.parse import urljoin
import requests
origin = "https://api.y2.dev"
url = f"{origin}/api/v2/finint?limit=500"
headers = {"Authorization": f"Bearer {os.environ['Y2_API_KEY']}"}
while url:
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
page = response.json()
warehouse.merge("y2_finint_facts", page["data"], key="id")
next_link = page["links"]["next"]
url = urljoin(origin, next_link) if next_link else None
```
`/api/v2/finint` does not offer NDJSON. If a row-oriented stream is required, use
`/api/v1/osint/finint?format=ndjson`; that endpoint returns at most 100 rows per request and
exposes continuation in `X-Y2-Next-Cursor`.
Model semantic fields as facts and dimensions. Do not make deprecated epoch aliases or raw
provider payloads part of the warehouse contract.
## Build a durable change pipeline
Use webhooks for low-latency report notifications and `/api/v2/changes` to detect missed or
updated resources. A bearer API key receives global intelligence changes plus changes belonging
to its own user or workspace; it does not receive another tenant's records.
```ts
async function ingestChanges(savedWatermark?: string) {
const first = new URL("https://api.y2.dev/api/v2/changes");
first.searchParams.set("limit", "500");
if (savedWatermark) first.searchParams.set("watermark", savedWatermark);
let next: URL | null = first;
while (next) {
const response = await requireSuccess(
await fetch(next, {
headers: { Authorization: `Bearer ${process.env.Y2_API_KEY}` },
}),
);
const page = await response.json();
// Implement this as one transaction: apply every resource change or
// tombstone, then persist the page watermark as the final write.
await applyPageAtomically(page.data, page.meta.watermark);
next = page.links.next
? new URL(page.links.next, "https://api.y2.dev")
: null;
}
}
```
The watermark is an exclusive checkpoint. Reusing the last committed value after a failure is
safe; saving it before resource writes commit can lose changes. A tombstone instructs the
consumer to remove or retire the named resource.
For webhook requests, verify HMAC-SHA256 over the exact raw request bytes before parsing JSON.
Deduplicate on the CloudEvent `id` (also sent as `Idempotency-Key` and `X-Y2-Event-Id`) before
performing side effects, and acknowledge a duplicate with a `2xx` response.
Y2 does not run an automatic retry loop for failed webhook deliveries. Use the change feed as
the repair path; five consecutive delivery failures disable the webhook configuration.
---
# Query the Intel API (v2) (/docs/api/intel)
Intel v2 turns normalized observations into stable incidents, entities, relationships, markets, and
decision signals. Use it when your integration needs to follow identity and relationships instead of
processing a raw event feed.
```mermaid
flowchart LR
A[List a focused collection] --> B[Persist public IDs]
B --> C[Drill into an incident or entity]
C --> D[Include bounded related resources]
C --> E[Traverse an entity graph]
```
The base URL is:
```text
https://api.y2.dev/api/v2
```
Explorer investigation boards are an in-app feature. The public Intel API exposes the ontology
resources that can inform an investigation, but it does not expose investigation-board CRUD
endpoints.
## Choose a collection
| Goal | Endpoint | Primary scope |
| --- | --- | --- |
| Find normalized incidents | `GET /incidents` | Depends on `category` |
| Search ontology entities | `GET /entities` | `intel:explorer` |
| Read a single entity | `GET /entities/{entityId}` | `intel:explorer` |
| Traverse relationships | `GET /entities/{entityId}/graph` | `intel:explorer` |
| Read prediction markets | `GET /markets` | `intel:finint` |
| Read financial indicators | `GET /finint` | `intel:finint` |
| Find extracted decision signals | `GET /signals` | Depends on `domain` |
| Traverse a cyber-focused graph | `GET /cyber/graph` | `intel:cyber` |
| Search CVEs | `GET /cyber/cves` | `intel:cyber` |
| Search threat actors | `GET /cyber/actors` | `intel:cyber` |
`GET /api/v2/changes` is the durable change feed for public intelligence resources. It uses
`osint:read`, not an Intel scope; see [Integration Recipes](/docs/api/integration-recipes) for a
checkpointed ingestion pattern.
## List incidents
Start with the narrowest category, lifecycle, severity, and time filters your application can use.
`sinceMs` is a lower bound on `lastObservedAt`, expressed in Unix milliseconds.
Incident timestamps answer different questions:
| Field | Meaning |
| --- | --- |
| `occurredAt` | When the incident happened or is forecast to happen |
| `firstObservedAt` | When Y2 first observed the incident |
| `lastObservedAt` | When Y2 most recently observed or refreshed the incident |
A forecast incident can have an `occurredAt` value years in the future while both observation
timestamps reflect when Y2 received the supporting information. Use `lastObservedAt`, not
`occurredAt`, as an incremental polling watermark.
```bash
curl --get "https://api.y2.dev/api/v2/incidents" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "category=cyber" \
--data-urlencode "severity=high" \
--data-urlencode "status=active" \
--data-urlencode "limit=50" \
--data-urlencode "fields[incidents]=title,severity,status,lastObservedAt"
```
List collections return `data`, `meta`, and `links`. Follow `links.next` until it is `null`; the
cursor is opaque and bound to the request's filters. Limits are clamped to `1–500`.
For incremental polling, convert the largest returned `lastObservedAt` value to Unix milliseconds
and pass it on the next request:
```bash
curl --get "https://api.y2.dev/api/v2/incidents" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "sinceMs=1786374126243" \
--data-urlencode "fields[incidents]=title,status,occurredAt,firstObservedAt,lastObservedAt"
```
## Drill into related context
An incident or entity detail request returns its primary resource by default. Use `include` to
request only the bounded related collections you need.
```bash
curl --get \
"https://api.y2.dev/api/v2/incidents/inc_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "include=observations,entities,primaryPlace" \
--data-urlencode "fields[incidents]=title,severity,status,lastObservedAt" \
--data-urlencode "fields[observations]=title,severity,observedAt,geometry"
```
Supported includes are `observations`, `markets`, `entities`, `primaryPlace`, and
`relatedIncidents`.
```bash
curl --get \
"https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "include=relations,relatedEntities,incidents" \
--data-urlencode "fields[entities]=kind,name,aliases,updatedAt"
```
Supported includes are `relations`, `relatedEntities`, `incidents`, `markets`, and
`observations`.
Sparse fieldsets use `fields[incidents]`, `fields[entities]`, `fields[markets]`,
`fields[observations]`, `fields[finint]`, or `fields[signals]` as appropriate. The server always
retains resource identity fields and rejects unsupported fields or includes. A request can contain
at most 24 values in each projection parameter.
## Traverse a graph
Use the general entity graph after resolving an `ent_...` ID from `/entities`. Its breadth-first
traversal accepts `depth=0–3`, supports a comma-separated `relationKinds` filter, and is capped at
200 nodes.
```bash
curl --get \
"https://api.y2.dev/api/v2/entities/ent_0123456789abcdef01234567/graph" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "depth=2" \
--data-urlencode "relationKinds=affects,targets,uses"
```
For a CVE, threat actor, or malware family, `/cyber/graph` offers a cyber-specific traversal. Pass
at least one of `rootCveId`, `rootActorId`, or `rootMalwareFamilyId`; its depth is limited to `1–2`.
## Select the correct scope
| Scope | Accepted operations |
| --- | --- |
| `intel:explorer` | Entities, entity details and graphs, unfiltered incidents, and general signals |
| `intel:finint` | Markets, FININT indicators, non-cyber categorized incidents, and markets or supply-chain signals |
| `intel:cyber` | Cyber graph, CVE and actor lists, cyber incidents, and cyber or technology signals |
When `/signals` has no `domain` filter, any Intel scope is accepted. Other signal domains require
`intel:explorer`. Authenticated requests can receive authorized private and workspace rows; x402
callers receive global and community rows only.
New API keys with Intel scopes require Pro or Elite. Existing Lite API keys remain valid under the
current grace policy, while Lite retains its in-app Intel surfaces. See [Plans and Limits](/docs/plans)
for the complete product-entitlement matrix.
## Authenticate or pay per request
Every Intel endpoint accepts a scoped bearer API key or its documented
[x402 payment flow](/docs/api/x402). A request without `Authorization` begins with `402 Payment
Required`; retry it with the signed payment header described by that endpoint. Do not send both a
bearer key and x402 payment for the same request.
## Intel v2 or OSINT v1?
| Use Intel v2 when you need | Use [OSINT v1](/docs/api/osint) when you need |
| --- | --- |
| Stable entity and incident identity | Broader normalized event feeds |
| Relationships and graph traversal | Source-health and source-specific surfaces |
| Bounded related-resource expansion | Country, regional, and geospatial feeds |
| Semantic FININT and decision signals | NDJSON or GeoJSON representations where documented |
## Endpoint reference
---
# Connect Y2 through MCP (/docs/api/mcp)
`@y2-intel/mcp` connects MCP-compatible clients to Y2 documentation, the OpenAPI contract,
bounded API reads, and Agent Y2. The documented release runs locally over stdio and calls Y2 over
HTTPS.
```mermaid
flowchart LR
A[MCP client] <-->|stdio| B[@y2-intel/mcp]
B -->|HTTPS| C[Y2 API]
B -->|HTTPS| D[Y2 docs and OpenAPI]
```
The MCP server is a separate public package; its implementation is not in the platform
repository. This page documents the intended integration contract. After installation, treat
MCP `tools/list`, `resources/list`, and `prompts/list` as the source of truth for that installed
package version.
## Before you begin
You need:
- Node.js with `npx`
- An MCP client that supports local stdio servers
- A Y2 API key with only the scopes the client needs
Create a separate key for each MCP client so it can be revoked and audited independently. Keep
`Y2_API_KEY` in the client environment or its protected configuration; never put it in a prompt or
tool argument.
## Configure your client
Add the stdio server from a shell where `Y2_API_KEY` is set:
```bash
codex mcp add y2 --env Y2_API_KEY="$Y2_API_KEY" -- npx -y @y2-intel/mcp
```
Confirm that the configuration exists:
```bash
codex mcp list
```
The command syntax above matches the locally installed Codex CLI. The `--env` value is saved
with the MCP server configuration, so protect the client configuration file as a secret.
Add the stdio server from a shell where `Y2_API_KEY` is set:
```bash
claude mcp add --transport stdio --env Y2_API_KEY="$Y2_API_KEY" y2 \
-- npx -y @y2-intel/mcp
```
Inspect the saved server:
```bash
claude mcp get y2
```
Add the server to the client's MCP configuration and restart the client:
```json
{
"mcpServers": {
"y2": {
"command": "npx",
"args": ["-y", "@y2-intel/mcp"],
"env": {
"Y2_API_KEY": "y2_..."
}
}
}
}
```
This configuration contains a plaintext credential. Restrict access to the configuration file
or use the client's supported secret-management facility.
## Configure package behavior
The documented package accepts these environment variables:
Override base URLs only for a Y2-controlled development or test environment. Do not point a
credentialed MCP process at an untrusted proxy.
## Verify the installed capabilities
Run the MCP Inspector before allowing an agent to call tools:
```bash
npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \
--method tools/list
npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \
--method resources/list
npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \
--method prompts/list
```
The documented release is expected to expose:
| Capability | Name | Y2 authorization |
| --- | --- | --- |
| Resource | `y2://docs/index` | None |
| Resource | `y2://docs/full` | None |
| Resource | `y2://openapi` | None |
| Resource | `y2://quickstart` | None |
| Tool | `y2_get_openapi_operation` | None |
| Tool | `y2_list_reports` | `reports:read` |
| Tool | `y2_get_report` | `reports:read` |
| Tool | `y2_list_news` | `news:read` |
| Tool | `y2_ask_agent` | `agent:y2` |
It also documents the prompts `integrate-y2-api`, `ask-y2-brief`, and
`debug-y2-api-call`. If inspection returns a different inventory, follow the schemas returned by
the installed server rather than guessing arguments from this list.
Test a no-key operation first:
```bash
npx -y @modelcontextprotocol/inspector --cli npx -y @y2-intel/mcp \
--method tools/call \
--tool-name y2_get_openapi_operation \
--tool-arg operationId=listReports
```
Then create a scoped key and test one bounded read such as `y2_list_reports`. Enable
`y2_ask_agent` only after read-only tools work as expected.
`agent:y2` allows the fixed Y2 agent to use tools available under the key's scopes and plan.
Those tools can change account resources. Omit this scope unless the MCP client is intended to
perform those actions.
## Troubleshoot startup
| Symptom | Check |
| --- | --- |
| Server exits immediately | Run `npx -y @y2-intel/mcp` in a terminal and inspect stderr |
| No Y2 server in the client | Confirm the client saved the configuration and restart it |
| Tool returns `401` | Replace a missing, malformed, revoked, or expired `Y2_API_KEY` |
| Tool returns `403` | Add the required scope to a new key; key scopes are fixed at creation |
| Tool returns `429` | Respect `Retry-After` and the rate-limit headers |
| Agent tool returns a credit error | Check Agent Y2 chat credits and plan access; it does not use x402 |
---
# Migrate to Canonical API Responses (/docs/api/migration-v1-1)
Use this guide to migrate a client that depends on legacy persistence-shaped fields or pagination
behavior. The v1 routes remain at `/api/v1`; their response contract was widened additively. Intel
v2 routes under `/api/v2` emit canonical public DTOs.
```mermaid
flowchart LR
A[Legacy reader] --> B[Read additive compatibility response]
B --> C[Persist public IDs and semantic time]
C --> D[Follow canonical pagination and errors]
D --> E[Remove legacy field reads]
```
Responses identify the route and schema contracts with `X-Y2-API-Version` and
`X-Y2-Schema-Version`. Current shared v1 response helpers emit API version `1.0` and schema version
`1.1`; v2 operations emit API version `2.0`.
## Migration checklist
### Capture current client fixtures
Save representative success, empty, error, and paginated responses before changing parsers.
Include response headers in the fixtures.
### Store public resource IDs
Replace saved Convex document IDs with the typed IDs emitted in current responses. Treat every
ID as opaque.
### Adopt canonical fields
Read RFC 3339 semantic timestamps, typed outcome rows, Problem Details, and the documented
missing-value states. Stop creating new dependencies on deprecated aliases.
### Replace collection traversal
Follow `links.next` or return `meta.page.nextCursor` unchanged. Do not infer another page from
the number of rows.
### Harden writes and delivery consumers
Add idempotency keys, ETag preconditions, CloudEvent deduplication, and a durable change-feed
checkpoint where those features apply.
## Replace persistence IDs with public IDs
| Resource | Prefix |
| --- | --- |
| Report | `rpt_` |
| Profile | `prf_` |
| Subscription | `sub_` |
| Webhook | `whk_` |
| Observation | `obs_` |
| Incident | `inc_` |
| Entity | `ent_` |
| Market | `mkt_` |
| Financial indicator row | `fin_` |
| Place | `plc_` |
| Relation | `rel_` |
| Signal | `sig_` |
| Source | `src_` |
Legacy Convex IDs remain accepted on migrated resource paths during the compatibility window, but
responses and webhooks emit public IDs. Store the exact ID Y2 returns. Do not derive IDs, parse the
hash, or depend on its current length.
## Traverse collections by link or cursor
Canonical collections return `data`, `meta`, and `links`. `meta.pageCount` is the number of rows in
the current page, not the collection total. A total exists only where a response explicitly defines
`totalCount`.
```ts
let url: string | null = "https://api.y2.dev/api/v2/incidents?limit=100";
while (url) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.Y2_API_KEY}` },
});
if (!response.ok) throw await response.json();
const page = await response.json();
await persist(page.data);
url = page.links.next
? new URL(page.links.next, "https://api.y2.dev").toString()
: null;
}
```
Cursors are opaque and filter-bound. Never edit them or reuse one with a different filter shape.
Changing `limit` does not change the filter shape, but following `links.next` is the safest default
because it preserves the server's continuation parameters.
## Distinguish omitted, null, and empty
| State | Meaning |
| --- | --- |
| Field omitted | Optional enrichment or bounded relation was not requested or not present |
| Field is `null` | The schema permits the field, but its value is unknown or unavailable |
| Field is `[]` | The relationship or outcome set is known and empty |
Numeric measurements include their documented unit, currency, or provider basis. Prediction
outcomes are objects with `label` and a `0–1` `probability`; do not parse JSON-encoded outcome
strings in a new client.
## Replace deprecated compatibility fields
| Surface | Deprecated | Canonical replacement |
| --- | --- | --- |
| Regional OSINT query | `since` | `datetime={RFC3339 start}/..` |
| Regional OSINT query | `until` | `datetime=../{RFC3339 end}` |
| Profile report metadata | `reportGeneratedAt` | `reportGeneratedAtISO` |
| Profile delivery metadata | `generatedAt` | `generatedAtISO` |
| Report audio metadata | `duration` | `durationSeconds` |
| Collection metadata | `meta.count` | `meta.pageCount` for this page |
The regional `since` and `until` parameters are inclusive epoch-millisecond bounds. Do not combine
either one with `datetime`. The compatibility fields remain additive on v1 for the published
migration window; new integrations should use only the canonical fields.
## Parse Problem Details
JSON failures use `application/problem+json` with RFC 9457-style top-level fields: `type`, `title`,
`status`, `detail`, `instance`, `code`, and `requestId`. A deprecated nested `error` object remains
for v1 compatibility.
```ts
if (!response.ok) {
const problem = await response.json();
logger.error({
code: problem.code,
status: problem.status,
requestId: problem.requestId,
});
throw new Error(problem.detail);
}
```
Retry only when the status and response headers indicate a retryable rate-limit or availability
failure. A validation, authentication, scope, idempotency, or precondition failure requires a
request change.
## Request specialized representations explicitly
- Send `Accept: text/markdown` to a report endpoint that advertises canonical Markdown.
- Send `format=ndjson` or `Accept: application/x-ndjson` only to collections whose OpenAPI
operation advertises NDJSON.
- Send `format=geojson` or `Accept: application/geo+json` only to spatial operations that advertise
a GeoJSON FeatureCollection.
Do not infer support from another collection. For example, `/api/v1/osint/finint` offers NDJSON,
while `/api/v2/finint` currently returns JSON only.
GeoJSON follows WGS 84 and uses `[longitude, latitude]`. For `/api/v1/osint/regional`, `bbox` is
`west,south,east,north`; `datetime` accepts an RFC 3339 instant or `start/end` interval, with `..`
as an open boundary.
## Make profile and webhook writes safe
`POST /profiles` and `POST /webhooks` return `201`, the created resource, `Location`, and `ETag`.
Both accept an `Idempotency-Key` containing 8–200 allowed characters. The key is scoped to the
tenant and operation and retained for 24 hours.
- Replaying the same key with the same canonical JSON body returns the original resource.
- Reusing the key with a different body returns `409 IDEMPOTENCY_CONFLICT`.
- `PUT` replaces mutable state; `PATCH` changes only supplied fields.
- Updates and deletes accept `If-Match`; a stale value returns `412 PRECONDITION_FAILED` with the
current `ETag`.
- A successful delete returns `204` with no JSON body.
## Migrate webhook consumers to CloudEvents
Report notifications use `Content-Type: application/cloudevents+json` and event type
`dev.y2.report.generated.v1`. The body contains compact report and subscription IDs, a bounded
summary, intelligence counts, audio availability, and API links. It excludes report HTML, storage
IDs, model names, prompts, and generation costs.
Deduplicate on the CloudEvent `id`, also sent as `Idempotency-Key` and `X-Y2-Event-Id`. When the
same logical event is attempted again, its event ID and body remain stable;
`X-Y2-Attempt-Id`, `X-Y2-Attempt`, and `X-Y2-Timestamp` identify the network attempt. Verify
`X-Y2-Signature` over the exact raw bytes before parsing JSON.
Y2 does not run an independent webhook retry loop. A failed attempt increments the consecutive
failure count, and five consecutive failures disable the webhook. Use the change feed to repair
missed state.
## Recover with the change feed
`GET https://api.y2.dev/api/v2/changes` returns append-only resource changes from an exclusive
watermark. Process each page, commit every resource update or tombstone, and persist
`meta.watermark` as the final write.
Deleted resources have `changeType: "deleted"` and `tombstone: true`. Authenticated keys receive
global intelligence changes plus their user or workspace changes. Anonymous x402 requests receive
global changes only. The current feed has no retention expiry, but consumers should still repair
continuously instead of treating it as a permanent archive guarantee.
---
# Build with the News API (/docs/api/news)
Use the News API to add current narrative context to dashboards, alerts, reports, or intelligence
pipelines. The API reads Y2's cached direct-source terminal data; it does not run a new source fetch
for each request.
```mermaid
flowchart LR
A[Discover feed IDs] --> B{Required output}
B -->|Individual items| C[Page /news]
B -->|Topic synthesis| D[Read /news/recaps]
C --> E[Join to OSINT or reports]
D --> E
```
The News base URL is:
```text
https://api.y2.dev/api/v1/news
```
## Authenticate
Send a bearer key with `news:read`:
```bash
curl "https://api.y2.dev/api/v1/news/feeds" \
--header "Authorization: Bearer $Y2_API_KEY"
```
All three News operations also support [x402 pay-per-request](/docs/api/x402). Without a bearer
key, the first request returns `402 Payment Required`; retry with the payment header described in
that response. Do not send a bearer key and x402 payment together.
## Discover feed IDs
Call `GET /feeds` instead of hard-coding the catalog. The current registry contains 40 topics
grouped across crypto, AI and technology, macro and finance, politics and geopolitics, industry,
and regional desks.
```bash
curl "https://api.y2.dev/api/v1/news/feeds" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Each `data` row contains:
| Field | Use |
| --- | --- |
| `id` | Machine-readable value for the `topics` query parameter |
| `name`, `shortLabel` | Full and compact display labels |
| `description` | Human-readable feed purpose |
| `group`, `groupLabel` | Picker grouping metadata |
| `color` | Current UI gradient classes; do not use as identity |
| `ingestOntology` | Whether eligible items can enter Y2's ontology pipeline |
`meta.defaultTopics` currently contains `crypto`, `geopolitics`, `macro`, `equities`, `ai`, and
`energy`. Omitting `topics` from item or recap requests uses that set.
## Page news items
Pass one or more comma-separated feed IDs. The server merges matching caches, removes duplicate
source items by their upstream ID, sorts newest first, and returns up to 200 rows.
```bash
curl --get "https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "topics=cyber,semiconductors,ai" \
--data-urlencode "limit=100"
```
The JSON response has `data`, `meta`, and `links`. Follow `links.next` until it is `null`; do not
edit the opaque cursor or reuse it with different topic filters.
A canonical news item includes:
Treat source URL, publisher, language, and retrieval metadata as nullable. A source record can be
identified even when the provider did not supply every attribution field.
## Stream rows as NDJSON
For a warehouse or line-oriented processor, request NDJSON. Each non-empty line is one canonical
news item; continuation moves to `X-Y2-Next-Cursor` because the body contains rows only.
```bash
curl --get "https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/x-ndjson" \
--data-urlencode "topics=geopolitics,energy" \
--data-urlencode "limit=200"
```
You can also send `format=ndjson`. Preserve the original topics and limit when sending the next
cursor.
## Retrieve topic recaps
Use recaps when an application needs synthesis rather than individual items. Supported timeframes
are `12h`, `24h`, `3d`, and `7d`; the default is `12h`.
```bash
curl --get "https://api.y2.dev/api/v1/news/recaps" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "topics=macro,equities,rates_fx" \
--data-urlencode "timeframe=24h"
```
`data` is an object keyed by topic ID. A requested topic can be absent when no valid cached recap
exists for that topic and timeframe. Use `meta.topics` and `meta.timeframe` to retain request
context; do not treat a missing topic as an empty recap.
`/news` and `/news/recaps` can return `503 CACHE_NOT_READY` before their respective caches have
any data. Retry with backoff. A successful empty or partially populated recap object means the
cache exists but not every requested topic has a current recap.
## Combine News with structured intelligence
News items are narrative observations. Use [OSINT v1](/docs/api/osint) for broader normalized
event, country, geospatial, and source-health surfaces. Use [Intel v2](/docs/api/intel) when you need
stable incidents, entities, relationships, FININT, or extracted decision signals.
---
# Use the OpenAPI Contract (/docs/api/openapi)
Y2 publishes one OpenAPI 3.1 document as the machine-readable contract for its REST API. The
generated endpoint reference, API Workbench, repository TypeScript declarations, and contract
tests all consume that source.
```mermaid
flowchart LR
A[public/api/openapi.yaml] --> B[Fumadocs endpoint pages]
A --> C[API Workbench]
A --> D[TypeScript declarations]
A --> E[Response fixtures and contract tests]
B --> F[Generated reference navigation]
```
Use the generated [Endpoint Reference](/docs/api/reference) for operation details. Use this page
to locate, consume, or update the raw specification. Do not manually edit files under
`content/docs/api/reference` or `src/lib/openapi-reference-nav.generated.ts`.
## Get the specification
The production URL is:
```text
https://y2.dev/api/openapi.yaml
```
The app serves the same repository file at `/api/openapi.yaml` in development and production.
[Download the OpenAPI specification](/api/openapi.yaml)
## Compose the correct request URL
The document contains more than one server because v1 and v2 use different path shapes.
| Operation shape | Server | Example result |
| --- | --- | --- |
| v1 path such as `/reports` | `https://api.y2.dev/api/v1` | `https://api.y2.dev/api/v1/reports` |
| v2 literal path such as `/api/v2/incidents` | `https://api.y2.dev` operation override | `https://api.y2.dev/api/v2/incidents` |
OpenAPI tooling should honor an operation- or path-level `servers` value before the document-level
servers. Avoid blindly prepending `/api/v1` to an Intel v2 path.
## Read Y2 extensions
In addition to standard OpenAPI fields, operations can declare:
The standard `security` array remains authoritative for allowed authentication alternatives. Agent
Y2 operations are bearer-key only. Operations with an empty security alternative and `x-x402`
begin the payment flow without `Authorization`. The sanitized x402 receipt lookup is public.
## Import into an API client
### Import the production URL
Import `https://y2.dev/api/openapi.yaml` into a client that supports OpenAPI 3.1.
### Configure the server
Confirm that the selected operation uses the intended v1 or v2 server. Some clients expose the
document's server list as an environment selector.
### Add a scoped credential
Store `Y2_API_KEY` in the client's secret or environment facility and configure bearer auth as
`Bearer {{Y2_API_KEY}}`. Never commit an exported collection containing the key.
### Validate a read
Start with a bounded GET whose required scope is on the key. Check `X-Request-Id`, API version,
schema version, and rate-limit headers as well as the body.
## Generate types or a client
For a TypeScript project, generate path and component types directly:
```bash
npx openapi-typescript https://y2.dev/api/openapi.yaml \
--immutable \
--alphabetize \
--output src/y2-api.d.ts
```
For a full client, use an OpenAPI 3.1-compatible generator and review its handling of operation-level
servers, streaming responses, `application/problem+json`, NDJSON, GeoJSON, and x402 extensions.
Generated code is a starting point; SSE and payment flows commonly need explicit transport code.
## Update the contract in this repository
The editable source is `public/api/openapi.yaml`.
### Change the source specification
Update the operation, schema, response, examples, scopes, and x402 metadata together. Keep
operation IDs stable unless the operation itself is intentionally replaced.
### Regenerate derived artifacts
```bash
bun run docs:openapi
bun run generate:api-types
bun run generate:api-contract-fixtures
```
These commands update the Fumadocs operation pages and nav, immutable TypeScript declarations,
and schema-derived response fixtures.
### Run the contract gate
```bash
bun run check:api-contract
bun run docs:source
```
The contract gate lints the OpenAPI file, checks generated types, verifies the 54-operation
manifest against Convex HTTP registrations, validates response fixtures against schemas, and
runs focused tests for IDs, pagination, representations, projections, middleware, and webhook
identity.
### Review generated scope
Confirm that generated changes correspond to the intended source edit. Never patch a generated
endpoint page to conceal a mismatch in `public/api/openapi.yaml`.
`generate:api-contract-fixtures` derives representative bodies from the schemas. The contract
tests prove schema consistency and prevent persistence-field leakage; they do not prove that a
deployed environment currently returns those exact values.
## Repository artifacts
---
# Query the OSINT API (/docs/api/osint)
The OSINT API exposes source-oriented Situation Room data as canonical public resources. Use it for
normalized event feeds, geospatial observations, country surfaces, provider health, prediction
markets, transport and security feeds, or financial indicators.
```mermaid
flowchart LR
A{Question} -->|What happened?| B[Events or regional search]
A -->|Where?| C[Map or GeoJSON]
A -->|Country context?| D[Country resources]
A -->|Provider healthy?| E[Source status]
A -->|Entity relationships?| F[Intel v2]
```
The base URL is:
```text
https://api.y2.dev/api/v1/osint
```
## Choose an endpoint
### Events and geography
| Goal | Endpoint | Useful filters |
| --- | --- | --- |
| List normalized threat events | `GET /events` | `category`, `severity` |
| Load only geolocated map events | `GET /map` | `region` |
| Compose regional and spatial filters | `GET /regional` | `q`, `region`, `countryCode`, `sourceType`, `bbox`, radius, time |
| Read report-extracted events | `GET /y2-events` | `category`, `severity`, `countryCode` |
### Country intelligence
| Goal | Endpoint |
| --- | --- |
| Periodically generated intelligence brief | `GET /countries/{countryCode}/brief` |
| Primary stock index and weekly change | `GET /countries/{countryCode}/markets` |
| Country-linked prediction markets | `GET /countries/{countryCode}/predictions` |
| Recent country events represented as news | `GET /countries/{countryCode}/news` |
| Country Conflict Indicators Index | `GET /countries/{countryCode}/cii` |
Country codes are ISO 3166-1 alpha-2 values such as `US` or `UA`.
### Specialized feeds
| Family | Endpoints |
| --- | --- |
| Conflict and security | `/cii`, `/cyber-threats`, `/military-posture`, `/gps-jamming` |
| Transport | `/aircraft`, `/vessels` |
| Markets | `/prediction-markets`, `/finint` |
| Operations | `/sources/status` |
Aircraft ingestion has been disabled since May 8, 2026, to reserve the shared Wingbits quota
for GPS interference detection. `/aircraft` and `/military-posture` can return empty or stale
data. `/gps-jamming` is unaffected.
## Authenticate or use x402
Every published OSINT operation accepts a bearer key with `osint:read` or its documented
[x402 pay-per-request flow](/docs/api/x402).
```bash
curl "https://api.y2.dev/api/v1/osint/events?limit=50" \
--header "Authorization: Bearer $Y2_API_KEY"
```
A request without `Authorization` begins with `402 Payment Required`. Retry it with the signed
payment header described in that response; do not send bearer and x402 credentials together.
## Search regional events
`/regional` combines text, source, category, severity, region, country, time, and coordinate
filters with AND semantics. Events without coordinates remain unless you request a bounding box,
radius, or `requireCoordinates=true`.
```bash
curl --get "https://api.y2.dev/api/v1/osint/regional" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "q=port disruption" \
--data-urlencode "sourceType=rss" \
--data-urlencode "category=economic" \
--data-urlencode "bbox=-10.0,35.0,40.0,65.0" \
--data-urlencode "datetime=2026-07-01T00:00:00Z/2026-07-21T23:59:59Z" \
--data-urlencode "limit=100"
```
`q` searches title and falls back to description substrings. `bbox` is exactly
`west,south,east,north` in WGS 84 and cannot cross the antimeridian. Radius search requires all
three of `nearLat`, `nearLon`, and `radiusKm`; the maximum radius is 20,000 km.
Use `datetime` for new integrations. It accepts an RFC 3339 instant or inclusive `start/end`
interval; `..` creates an open boundary. The inclusive epoch-millisecond `since` and `until`
parameters are deprecated and cannot be combined with `datetime`.
## Choose JSON, NDJSON, or GeoJSON
Representation support is operation-specific and listed on each generated reference page.
JSON collection responses contain `data`, `meta`, and `links`. Follow `links.next` until it is
`null`.
```bash
curl "https://api.y2.dev/api/v1/osint/cyber-threats?limit=100" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Supported collection routes return one canonical `data` row per line. Read continuation from
`X-Y2-Next-Cursor`.
```bash
curl "https://api.y2.dev/api/v1/osint/finint?format=ndjson&limit=100" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Spatial routes return an RFC 7946 FeatureCollection. Coordinates are longitude-first, and the
next page is the item in `links` whose `rel` is `next`.
```bash
curl "https://api.y2.dev/api/v1/osint/map?format=geojson&limit=200" \
--header "Authorization: Bearer $Y2_API_KEY"
```
You can use `Accept: application/x-ndjson` or `Accept: application/geo+json` instead of `format`.
Do not assume that every OSINT operation supports every representation: country briefs, country
stock indices, country CII, and source status return JSON only.
## Page collections safely
JSON collections expose an opaque, filter-bound cursor through `meta.page.nextCursor` and
`links.next`. Specialized representations move the cursor to `X-Y2-Next-Cursor` so their body can
stay representation-native. Preserve the original filters on the next request.
The maximum `limit` depends on the operation: for example, `/map` allows 500, `/regional` allows
200, and country prediction pages allow 10. Use the generated reference instead of assuming one
global maximum.
## Check source health before declaring absence
If a feed is empty or unexpectedly stale, inspect `/sources/status`:
```bash
curl "https://api.y2.dev/api/v1/osint/sources/status" \
--header "Authorization: Bearer $Y2_API_KEY"
```
| Field | Meaning |
| --- | --- |
| `state` | `closed` normally permits fetching; `open` isolates a failing source; `half_open` is a recovery probe state |
| `failureCount` | Consecutive failures; a successful fetch resets it to zero |
| `lastSuccessAt`, `lastSuccessAtISO` | Last success in epoch milliseconds and RFC 3339; nullable before any success |
| `lastFailureAt`, `lastFailureAtISO` | Most recent failure in both time formats |
| `lastError` | Most recent provider error when available |
Live-provider circuit breakers open after five consecutive failures and permit one probe after a
five-minute cooldown. A `y2_report` row represents report-enrichment health rather than a polled
provider: `open` means the newest recorded enrichment error is later than the latest success.
A healthy source does not prove completeness, and an empty endpoint does not prove that no
real-world event occurred. Preserve source attribution and corroborate high-impact conclusions.
## OSINT v1 or Intel v2?
| Use OSINT v1 when you need | Use [Intel v2](/docs/api/intel) when you need |
| --- | --- |
| Source-oriented normalized observations | Stable entity and incident identity |
| Country and provider-specific surfaces | Relationship and graph traversal |
| GeoJSON or NDJSON where advertised | Sparse semantic projections and bounded relations |
| Provider health and circuit-breaker state | Intel-specific FININT, cyber, or Explorer scopes |
---
# Manage Profiles through the API (/docs/api/profiles)
Profiles define recurring intelligence research: topic, schedule, status, instructions, and report
configuration. Subscriptions connect a profile to a delivery method. The API represents them as
separate resources.
```mermaid
flowchart LR
A[Profile prf_...] -->|schedule generates| B[Reports rpt_...]
C[Subscription sub_...] -->|follows| A
C -->|delivery preference| D[Email, SMS, or webhook]
```
`GET /api/v1/profiles` lists active subscriptions in the API key's user or workspace context.
Every row contains `subscription` and `profile`; it is not a flat catalog of every public
profile.
## Required scopes
| Operation | Scope |
| --- | --- |
| List subscribed profiles | `profiles:read` |
| Create, replace, patch, or delete an owned profile | `profiles:write` |
| Change subscription delivery or manage webhook configurations | `webhooks:manage` |
Profile creation and configuration features still follow the active workspace's
[plan limits](/docs/plans). API write access does not bypass profile, audio, branding, workspace,
or delivery entitlements.
## List subscriptions and profiles
```bash
curl "https://api.y2.dev/api/v1/profiles" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Each row has this shape:
```json
{
"subscription": {
"id": "sub_0123456789abcdef01234567",
"profileId": "prf_0123456789abcdef01234567",
"active": true,
"delivery": {
"method": "email",
"emailAudience": "individual",
"webhookId": null
}
},
"profile": {
"id": "prf_0123456789abcdef01234567",
"name": "Critical Supplier Risk",
"status": "active",
"frequency": "daily"
}
}
```
`profile` can be `null` if the subscribed resource no longer resolves. Save both IDs: `prf_...`
identifies research configuration, while `sub_...` identifies delivery preferences.
The list is not cursor-paginated today: `links.next` and `meta.page.nextCursor` are always `null`.
## Create an owned profile
Creation requires `name`, `topic`, `frequency`, and `scheduleTimeOfDay`. Times are UTC. The server
creates the profile as `active`, schedules it, and creates an active subscription for the same
tenant context.
```bash
curl "https://api.y2.dev/api/v1/profiles" \
--request POST \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: supplier-risk-2026-07-21" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting semiconductor suppliers, ports, export controls, severe weather, labor actions, and cyber incidents.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00",
"tags": ["supply-chain", "semiconductors"]
}'
```
Supported frequencies are `daily`, `weekly`, `biweekly`, and `monthly`. Weekly and biweekly
profiles can supply `scheduleDayOfWeek`; monthly profiles can supply `scheduleDayOfMonth`. See
[Scheduling](/docs/profiles/scheduling) for the product semantics, including the twice-monthly
biweekly schedule.
A successful create returns `201` with:
- the canonical `prf_...` resource in `data`
- `Location` containing its canonical API path
- `ETag` for that representation
- the supplied `Idempotency-Key`, when present
Idempotency keys must contain 8–200 letters, numbers, `.`, `_`, `:`, or `-`. They are tenant- and
operation-scoped for 24 hours. Reusing a key with a different canonical body returns
`409 IDEMPOTENCY_CONFLICT`.
## Patch one or more fields
Use `PATCH` when omitted fields must remain unchanged.
```bash
curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \
--request PATCH \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "If-Match: $PROFILE_ETAG" \
--data '{
"status": "paused",
"tags": ["supply-chain", "semiconductors", "paused-review"]
}'
```
Mutable fields include the profile's name, topic, status, schedule inputs, community visibility,
tags, custom instructions, report structure, search/model/budget/recursion/freshness/audio/tool
configuration, and branding template. The generated reference defines each nested input.
`PUT` and `PATCH /profiles/{profileId}` change profile configuration only. Use the subscription
delivery endpoint with the `sub_...` ID to select email, SMS, or a webhook.
## Replace mutable state
Use `PUT` only when sending the full intended mutable profile state. It requires the same four
fields as creation. Optional mutable fields omitted from a replacement reset to their defaults or
are cleared.
```bash
curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \
--request PUT \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor current disruption across strategic semiconductor supply chains.",
"frequency": "weekly",
"scheduleTimeOfDay": "08:00",
"scheduleDayOfWeek": "monday",
"tags": ["supply-chain"]
}'
```
Use the `ETag` returned by create or a previous update as `If-Match` when preventing a lost update.
`If-Match` is optional; a stale value returns `412 PRECONDITION_FAILED` and the current ETag.
There is currently no `GET /profiles/{profileId}` operation that returns an ETag.
## Change delivery on the subscription
Read `subscription.id` from `GET /profiles`, then call:
```bash
curl "https://api.y2.dev/api/v1/subscriptions/$SUBSCRIPTION_ID/delivery" \
--request PATCH \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"deliveryMethod": "webhook",
"webhookConfigId": "whk_0123456789abcdef01234567"
}'
```
Delivery methods are `email`, `sms`, `webhook`, and `both_email_sms`. A webhook delivery requires
an active, authorized `whk_...` configuration. For email-capable delivery, `emailAudience` can be
`individual` or, on a plan with member seats, `workspace`.
## Retrieve reports for a profile
The profile's `links.reports` value is the canonical filtered collection. You can also compose it
directly:
```bash
curl --get "https://api.y2.dev/api/v1/reports" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "profileId=$PROFILE_ID" \
--data-urlencode "limit=1"
```
This requires `reports:read` in addition to whichever profile scopes the client uses.
## Delete an owned profile
```bash
curl "https://api.y2.dev/api/v1/profiles/$PROFILE_ID" \
--request DELETE \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "If-Match: $PROFILE_ETAG"
```
Deletion is permanent. It cancels the scheduled job and deletes the profile's subscriptions,
signals, reports, and stored audio before returning `204` with no body. The API returns `404` when
the key cannot access or mutate the named profile, which avoids revealing another tenant's
resource.
In a shared workspace, write-role membership is required. Non-admin members can update or delete
only profiles they created; workspace administrators can manage tenant-scoped profiles.
---
# Projects API (/docs/api/projects)
Projects are owner-private intelligence workspaces. API requests derive the user and workspace from
the bearer key; callers cannot select another workspace in the request body.
## Scopes
| Operation | Scope |
| --- | --- |
| List or get Projects | `projects:read` |
| Create, update, archive, restore, or pin Projects | `projects:write` |
Both scopes require a workspace-bound API key. New API keys are available on Pro and Elite, even
though Projects are also available in the Lite application.
## Create a Project
```bash
curl -sS "https://api.y2.dev/api/v1/projects" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: project-supply-chain-2026-07" \
-d '{
"name": "Supply chain watch",
"objective": "Track disruptions affecting critical suppliers",
"instructions": "Prefer primary sources and call out conflicting evidence."
}'
```
The response returns a stable `prj_...` ID, `Location`, and `ETag`. Reusing an
`Idempotency-Key` with the same canonical JSON body returns the original Project.
## Read and update
```bash
curl -sS "https://api.y2.dev/api/v1/projects?status=active&limit=20" \
-H "Authorization: Bearer $Y2_API_KEY"
```
Project collections use the same opaque `nextCursor` and `links.next` pagination contract as other
Y2 collections. Send the returned cursor unchanged to retrieve older Projects.
Use `PATCH /api/v1/projects/{projectId}` for metadata, view defaults, pinning, and reversible
lifecycle changes. Send the latest `ETag` in `If-Match` when coordinating multiple writers.
```bash
curl -sS -X PATCH "https://api.y2.dev/api/v1/projects/$PROJECT_ID" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Content-Type: application/json" \
-H "If-Match: $PROJECT_ETAG" \
-d '{"status":"archived"}'
```
Archiving keeps Project data and pauses active Automations bound to that Project. Restore it with
`{"status":"active"}`.
Exact paths, parameters, request bodies, responses, and schemas
App views, private knowledge, evidence, resources, and lifecycle behavior
---
# OpenAI-compatible Agent Y2 chat stream (/docs/api/reference/agent-y2/createY2OpenAIChatCompletion)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Agent Y2 (/docs/api/reference/agent-y2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Stream a Y2 Agent response
OpenAI-compatible Agent Y2 chat stream
---
# Stream a Y2 Agent response (/docs/api/reference/agent-y2/streamAgentY2Chat)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Create an Automation (/docs/api/reference/automations/createAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get an Automation definition (/docs/api/reference/automations/getAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Automations (/docs/api/reference/automations)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List Automation definitions
Create an Automation
Get an Automation definition
Update or archive an Automation
List Automation run history
Request a manual Automation run
---
# List Automation run history (/docs/api/reference/automations/listAutomationRuns)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List Automation definitions (/docs/api/reference/automations/listAutomations)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update or archive an Automation (/docs/api/reference/automations/patchAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Request a manual Automation run (/docs/api/reference/automations/runAutomation)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Global Knowledge (/docs/api/reference/global-knowledge)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Retrieve Y2 Global Knowledge
---
# Retrieve Y2 Global Knowledge (/docs/api/reference/global-knowledge/retrieveGlobalKnowledgeV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Endpoint Reference (/docs/api/reference)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
API-key streams for the preconfigured Y2 Agent and compatibility endpoint
Report retrieval, text, audio, signals, and ontology graphs
Profile subscriptions, ownership, and configuration
Owner-private intelligence workspaces in the API key workspace
Durable Agent Y2 Automation definitions and run history
Webhook configuration for paid workspaces
Subscription delivery management
News Terminal items, feeds, and AI recaps
Situation Room events, feeds, country data, and source health
Ontology-backed v2 incidents, entities, markets, financial indicators, emergent signals, and cyber graphs.
Bounded, provider-neutral semantic and lexical retrieval across authorized Y2 reports and signal assessments.
x402 receipt lookup
---
# Cyber entity graph (/docs/api/reference/intel/getCyberGraphV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Traverse an entity relationship graph (/docs/api/reference/intel/getEntityGraphV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get an intel entity (/docs/api/reference/intel/getEntityV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get an intel incident (/docs/api/reference/intel/getIncidentV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Intel (/docs/api/reference/intel)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Read the public intelligence change feed
List intel incidents
Get an intel incident
List intel entities
Get an intel entity
Traverse an entity relationship graph
List intel markets
List FININT indicators
List tagged emergent signals
Cyber entity graph
List CVE entities
List threat actor entities
---
# List CVE entities (/docs/api/reference/intel/listCVEsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Read the public intelligence change feed (/docs/api/reference/intel/listChangesV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List intel entities (/docs/api/reference/intel/listEntitiesV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List FININT indicators (/docs/api/reference/intel/listFinintIntelV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List intel incidents (/docs/api/reference/intel/listIncidentsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List intel markets (/docs/api/reference/intel/listMarketsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List tagged emergent signals (/docs/api/reference/intel/listSignalsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List threat actor entities (/docs/api/reference/intel/listThreatActorsV2)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# News (/docs/api/reference/news)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List news items
List AI-generated recaps
List available news feeds
---
# List available news feeds (/docs/api/reference/news/listFeeds)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List news items (/docs/api/reference/news/listNews)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List AI-generated recaps (/docs/api/reference/news/listRecaps)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# AI-generated intelligence brief (/docs/api/reference/osint/getCountryBrief)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Country instability index (/docs/api/reference/osint/getCountryCII)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Country-specific news (/docs/api/reference/osint/getCountryNews)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Prediction markets (/docs/api/reference/osint/getCountryPredictions)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Stock market index (/docs/api/reference/osint/getCountryStockIndex)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# OSINT (/docs/api/reference/osint)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List threat events
List map events with coordinates
Conflict Indicators Index
AI-generated intelligence brief
Stock market index
Prediction markets
Country-specific news
Country instability index
Military posture by theater
Tracked military aircraft
Naval vessel positions
GPS interference zones
Data source health
List cyber threat indicators
List prediction markets
Y2 report-extracted events
Search regional OSINT events
Financial intelligence indicators
---
# Tracked military aircraft (/docs/api/reference/osint/listAircraft)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Conflict Indicators Index (/docs/api/reference/osint/listCII)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List cyber threat indicators (/docs/api/reference/osint/listCyberThreats)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Financial intelligence indicators (/docs/api/reference/osint/listFinintIndicators)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# GPS interference zones (/docs/api/reference/osint/listGPSJamming)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Military posture by theater (/docs/api/reference/osint/listMilitaryPosture)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List threat events (/docs/api/reference/osint/listOsintEvents)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List map events with coordinates (/docs/api/reference/osint/listOsintMapEvents)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Search regional OSINT events (/docs/api/reference/osint/listOsintRegionalEvents)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# List prediction markets (/docs/api/reference/osint/listPredictionMarkets)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Data source health (/docs/api/reference/osint/listSourceStatus)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Naval vessel positions (/docs/api/reference/osint/listVessels)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Y2 report-extracted events (/docs/api/reference/osint/listY2Events)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# x402 receipt lookup (/docs/api/reference/payments/getX402Receipt)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Payments (/docs/api/reference/payments)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
x402 receipt lookup
---
# Create an intelligence profile (/docs/api/reference/profiles/createProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Delete a profile (/docs/api/reference/profiles/deleteProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Profiles (/docs/api/reference/profiles)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List subscribed profiles
Create an intelligence profile
Update a profile (partial)
Delete a profile
Update a profile (full replacement)
---
# List subscribed profiles (/docs/api/reference/profiles/listProfiles)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update a profile (partial) (/docs/api/reference/profiles/patchProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update a profile (full replacement) (/docs/api/reference/profiles/updateProfile)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Create a Project (/docs/api/reference/projects/createProject)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get a Project (/docs/api/reference/projects/getProject)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Projects (/docs/api/reference/projects)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List owned Projects
Create a Project
Get a Project
Update, archive, or restore a Project
---
# List owned Projects (/docs/api/reference/projects/listProjects)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update, archive, or restore a Project (/docs/api/reference/projects/patchProject)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get a report (/docs/api/reference/reports/getReport)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report audio (/docs/api/reference/reports/getReportAudio)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get TTS-preprocessed report text (/docs/api/reference/reports/getReportAudioText)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report ontology graph (/docs/api/reference/reports/getReportGraph)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report emergent signals (/docs/api/reference/reports/getReportSignals)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Get report as plain text (/docs/api/reference/reports/getReportText)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Reports (/docs/api/reference/reports)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List intelligence reports
Get a report
Get report emergent signals
Get report ontology graph
Get report audio
Get report as plain text
Get TTS-preprocessed report text
---
# List intelligence reports (/docs/api/reference/reports/listReports)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Subscriptions (/docs/api/reference/subscriptions)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
Update subscription delivery method
---
# Update subscription delivery method (/docs/api/reference/subscriptions/updateDelivery)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Create a webhook configuration (/docs/api/reference/webhooks/createWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Delete a webhook configuration (/docs/api/reference/webhooks/deleteWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Webhooks (/docs/api/reference/webhooks)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
List webhook configurations
Create a webhook configuration
Delete a webhook configuration
Update a webhook configuration
Test a webhook endpoint
---
# List webhook configurations (/docs/api/reference/webhooks/listWebhooks)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Test a webhook endpoint (/docs/api/reference/webhooks/testWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Update a webhook configuration (/docs/api/reference/webhooks/updateWebhook)
{/* This file was generated by Fumadocs OpenAPI. Run `bun run docs:openapi` to update it. */}
export default function Layout(props) {
const { APIPage, OpenAPIPage } = props.components ?? {};
// "APIPage" is the old name from v10, this allows both for backward compatibility
const Comp = OpenAPIPage ?? APIPage;
return (
<>
{props.children}
>
);
}
---
# Retrieve Reports through the API (/docs/api/reports)
Use the Reports API to retrieve published intelligence generated for profiles. Start with compact
report metadata, then request only the representation or extracted artifact your integration needs.
```mermaid
flowchart LR
A[List reports] --> B[Persist rpt_ ID]
B --> C{Consumer}
C -->|Agent| D[Markdown + cited sources]
C -->|Application| E[Compact or expanded JSON]
C -->|Analytics| F[Signals or graph]
C -->|Human| G[Plain text or audio]
```
## Required scopes
| Operation | Bearer scope |
| --- | --- |
| List, detail, Markdown, plain text, signals, graph, and TTS-preprocessed text | `reports:read` |
| Audio metadata or CDN redirect | `reports:audio` |
The audio operation also depends on audio entitlement and an available recording. Every Report
operation supports [x402 pay-per-request](/docs/api/x402) as an alternative to a bearer key.
## List reports
For a bearer-key request without `profileId`, Y2 lists reports newest first across the active
profiles subscribed in that key's user or workspace context.
```bash
curl --get "https://api.y2.dev/api/v1/reports" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "limit=20"
```
To target one profile, pass its canonical `prf_...` ID:
```bash
curl --get "https://api.y2.dev/api/v1/reports" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "profileId=$PROFILE_ID" \
--data-urlencode "limit=20"
```
The endpoint supports only `profileId`, `limit`, `cursor`, and `format`; it does not provide a date
range filter. `limit` defaults to 20 and is capped at 100.
An anonymous x402 caller has no subscription context, so `profileId` is required for
`GET /reports`. Single-report and subresource calls identify the resource with `reportId`.
JSON pages return `data`, `meta`, and `links`. Follow `links.next` until it is `null`. For NDJSON,
send `format=ndjson` or `Accept: application/x-ndjson`; each line is one compact report and the next
cursor is in `X-Y2-Next-Cursor`.
## Read compact report metadata
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The default response contains the canonical `rpt_...` and `prf_...` IDs, topic, summary, published
timestamps, language, signal and graph counts, audio availability, profile identity, and links to
each representation. It does not include the full report body or full source rows by default.
An authenticated detail or subresource request must be subscribed to the report's profile. A
missing report returns `404`; an authenticated key without that subscription returns `403`.
## Expand structured JSON
Use the comma-separated `include` parameter to embed bounded related data.
| Include | Added field |
| --- | --- |
| `content` | `content.markdown` and its media type |
| `sources` | Canonical source records with `src_...` IDs and available attribution |
| `signals` | Report-local emergent decision signals |
| `graph` | Report-local ontology nodes, edges, incidents, and citations when available |
| `audio` | Audio representation metadata or `null` |
```bash
curl --get "https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--data-urlencode "include=content,sources,signals"
```
`view=agent` is shorthand for `content,sources,signals`. It does not add the ontology graph or
audio representation.
## Choose Markdown or plain text
Request the canonical stored report representation:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/markdown"
```
`format=markdown` is equivalent. This returns the report body directly, not a JSON envelope.
Request content with HTML stripped:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/text" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/plain"
```
Omit `Accept: text/plain` to receive JSON with `text`, `wordCount`, and an estimated
`readingTimeMinutes` at approximately 200 words per minute.
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/text" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Use Markdown for grounded agents and publishing pipelines. Use `/text` when the consumer cannot
render markup or needs reading-time metadata.
## Retrieve signals or the report graph
The subresources avoid transferring the full report body:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/signals" \
--header "Authorization: Bearer $Y2_API_KEY"
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/graph" \
--header "Authorization: Bearer $Y2_API_KEY"
```
Signals include decision fields, confidence, time horizon, normalized subjects, entity links when
resolved, tags, and cited sources. Reports created before signal extraction return an empty
`signals` array.
The graph is a bounded snapshot extracted from that report, not a live traversal of the full Intel
ontology. It contains report-local nodes, relationships, incident anchors, citations, and counts.
Reports created before graph extraction return `graph: null` and zero counts.
## Retrieve narration or TTS text
Request audio metadata with `reports:audio`:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/audio" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The JSON body contains the MP3 URL, duration, formatted duration, media type, and file size when
known. To redirect directly to the CDN asset, pass `redirect=true` or send an `Accept` header that
contains an audio media type. The redirect is `302`, and the redirect response can be cached for
24 hours. A report without audio returns `404 NO_AUDIO`.
`/audio-text` requires `reports:read`, not `reports:audio`. It returns the exact preprocessing path
used for narration, including pronunciation normalization and applicable profile branding:
```bash
curl "https://api.y2.dev/api/v1/reports/$REPORT_ID/audio-text" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The duration fields on this endpoint are estimates based on character count; they are not measured
audio duration.
Reports, summaries, signals, and ontology graphs are synthesized intelligence. Preserve source
URLs and confidence fields, and verify high-impact conclusions against primary evidence.
---
# Configure Report Webhooks (/docs/api/webhooks)
Use a webhook when your application should react as soon as Y2 publishes a report. This guide
covers configuration and consumption through the public API. For the delivery product overview,
see [Webhook delivery](/docs/delivery/webhooks).
```mermaid
flowchart LR
A[Create whk_ config] --> B[Test endpoint]
B --> C[Attach to sub_ subscription]
C --> D[Receive CloudEvent]
D --> E[Verify signature]
E --> F[Deduplicate by event ID]
F --> G[Fetch rpt_ representations]
```
## Before you begin
You need:
- a paid workspace plan with webhook delivery;
- an API key with `webhooks:manage`;
- a public HTTPS endpoint that accepts `POST` requests; and
- a secret generated and stored by your application if you want signed deliveries.
Y2 rejects loopback, private, link-local, cloud metadata, multicast, and other reserved network
destinations. In production, webhook URLs must use HTTPS. Custom headers must have string values;
`Host`, `Content-Length`, and `Content-Type` cannot be configured.
## 1. Create a webhook configuration
Send a stable `Idempotency-Key` when a client may retry the request:
```bash
curl --request POST "https://api.y2.dev/api/v1/webhooks" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: create-primary-webhook-01" \
--data '{
"name": "Primary report receiver",
"url": "https://example.com/webhooks/y2",
"secret": "replace-with-a-random-secret",
"headers": {
"X-Integration": "intelligence-pipeline"
}
}'
```
A successful request returns `201`, a canonical `whk_...` ID, `Location`, and `ETag`. Store the ID;
you need it to test the endpoint and attach it to a subscription.
List and mutation responses reveal whether signing is configured and return custom header names,
but never return the secret or custom header values.
## 2. Test the receiver
```bash
curl --request POST "https://api.y2.dev/api/v1/webhooks/$WEBHOOK_ID/test" \
--header "Authorization: Bearer $Y2_API_KEY"
```
The test request is a CloudEvents 1.0 event with type `dev.y2.webhook.test.v1`. Y2 waits up to five
seconds. A `2xx` response from your receiver produces `200`; a timeout, network error, or non-`2xx`
response produces `422`.
The test verifies connectivity and signing, but it does not attach the configuration to a report
subscription.
## 3. Attach the webhook to a subscription
Use the subscription's canonical `sub_...` ID, not its profile ID:
```bash
curl --request PATCH \
"https://api.y2.dev/api/v1/subscriptions/$SUBSCRIPTION_ID/delivery" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--data "{\"deliveryMethod\":\"webhook\",\"webhookConfigId\":\"$WEBHOOK_ID\"}"
```
The webhook must be active and belong to the same user or workspace scope as the subscription.
Changing a profile does not change its subscription delivery settings.
## 4. Process the report event
Production deliveries use structured CloudEvents with media type
`application/cloudevents+json` and event type `dev.y2.report.generated.v1`:
```json
{
"specversion": "1.0",
"id": "y2mexamplelogicalevent01",
"source": "https://api.y2.dev/api/v1/profiles/prf_0123456789abcdef01234567",
"type": "dev.y2.report.generated.v1",
"subject": "reports/rpt_0123456789abcdef01234567",
"time": "2026-07-21T18:00:00.000Z",
"datacontenttype": "application/json",
"dataschema": "https://api.y2.dev/schemas/events/report-generated-v1.json",
"data": {
"report": {
"id": "rpt_0123456789abcdef01234567",
"profileId": "prf_0123456789abcdef01234567",
"summary": "A concise report summary.",
"status": "published",
"generatedAt": "2026-07-21T17:59:42.000Z",
"language": "en",
"intelligence": { "signalCount": 3, "graphNodeCount": 12 },
"audio": { "status": "available", "durationSeconds": 284 }
},
"subscription": { "id": "sub_0123456789abcdef01234567" },
"links": {
"report": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567",
"markdown": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?format=markdown",
"sources": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?include=sources",
"signals": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/signals",
"graph": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/graph",
"audio": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/audio"
}
}
}
```
The event is intentionally compact. Use the supplied links and a key with the relevant Reports
scope to retrieve full Markdown, sources, signals, graph, or audio.
## Delivery headers
| Header | Meaning |
| --- | --- |
| `Content-Type` | `application/cloudevents+json` |
| `User-Agent` | `Y2-Webhook-Delivery/3.0` |
| `X-Y2-Timestamp` | Attempt time as Unix seconds |
| `X-Y2-Event-Id` | Stable logical event ID; matches the body `id` |
| `X-Y2-Attempt-Id` | Unique transport-attempt ID |
| `X-Y2-Attempt` | One-based attempt number |
| `Idempotency-Key` | Stable logical event ID |
| `X-Y2-Signature` | `sha256=` when a secret is configured |
Configured custom headers are sent first. Y2's delivery headers take precedence if names collide.
## Verify the signature
Compute HMAC-SHA256 over the exact raw request bytes. Do not parse and reserialize the JSON before
verification.
```typescript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyY2Webhook(
rawBody: Buffer,
signatureHeader: string | null,
secret: string,
): boolean {
if (!signatureHeader) return false;
const expected = Buffer.from(
`sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`,
);
const actual = Buffer.from(signatureHeader);
return actual.length === expected.length && timingSafeEqual(actual, expected);
}
```
Also compare `X-Y2-Timestamp` with your current time and reject values outside your replay window;
five minutes is a practical default. Only return `2xx` after the event has been durably accepted.
## Handle attempts and failures
The event body and `X-Y2-Event-Id` remain stable if the same logical delivery is attempted again.
`X-Y2-Attempt-Id` and `X-Y2-Attempt` identify the transport attempt. Deduplicate on the body `id`,
`X-Y2-Event-Id`, or `Idempotency-Key` before starting downstream work.
Production delivery times out after 10 seconds. Y2 does not run an independent HTTP retry loop for
a failed attempt, although a replayed delivery workflow can attempt the same persisted logical event
again. Five consecutive failed attempts automatically disable the webhook; a successful attempt
resets the failure counter.
`GET /webhooks` exposes `status`, `deliveryHealth.consecutiveFailures`, and
`deliveryHealth.lastUsedAt`. To re-enable a destination, replace its complete configuration with
`PUT` and set `isActive` to `true`, then test it again.
## Replace or delete a configuration
`PUT /webhooks/{webhookId}` replaces the complete mutable configuration. `name` and `url` are
required, and omitted optional fields reset to defaults. Send the latest returned `ETag` in
`If-Match` when you have one to prevent overwriting a concurrent change.
Before deleting a webhook, move every attached subscription to another delivery method or webhook.
Deletion returns `409 WEBHOOK_IN_USE` while any subscription still references the configuration;
a successful deletion returns `204` with no body.
---
# Explore the API with Workbench (/docs/api/workbench)
The API Workbench turns Y2's live OpenAPI description into a browsable endpoint catalog, parameter
editor, request runner, and snippet generator.
Open **[Developers → API Workbench](/app/developers/api-workbench)** in the dashboard.
New API access is available on Pro and Elite. Existing grandfathered Lite API users retain
Workbench access during the grace period.
```mermaid
flowchart LR
A[Open endpoint category] --> B[Choose an operation]
B --> C[Set path and query values]
C --> D[Run with pasted key]
C --> E[Copy secret-safe snippet]
D --> F[Inspect status and body]
E --> G[Move into your codebase]
```
## What Workbench reads from OpenAPI
The page loads `/api/openapi.yaml` and uses the current contract to show:
- endpoint categories, methods, paths, summaries, and operation IDs;
- required bearer scopes from `x-required-scopes`;
- path and query parameter descriptions, types, enums, examples, and defaults; and
- operation- or path-level server overrides, including the Intel v2 base URL.
Parameters use enum selectors, boolean toggles, and numeric or text inputs where applicable.
Required path and query values are checked before a live request leaves the browser.
The current Workbench editor does not render OpenAPI request-body fields. Use it directly for
`GET`, bodyless `POST`, and other path/query-driven operations. For profile creation, webhook
configuration, delivery updates, and other JSON-body mutations, start from the generated
endpoint reference or the relevant API guide.
## Run a read request
### Paste an API key
Enter a key in the password field at the top of the page. The value remains in React component
state and is added as `Authorization: Bearer ...` only when you run a request.
### Open a category and endpoint
Categories begin collapsed. Open one such as **Intel**, **OSINT**, **Reports**, or **News**, then
choose an endpoint. The row shows its method, path, scope, parameter counts, and operation ID.
### Fill required values
Path parameters are always required. Required query parameters are shown separately; enable
only the optional query parameters you want to send.
### Run the request
Select **Run request**. Workbench calls the canonical production API from your browser and shows
the response status and parsed body inline. JSON is formatted; non-JSON bodies are shown as text.
If a required path or query value is missing, Workbench produces a local `400` explanation instead
of sending an incomplete request. A missing key produces a local `401` prompt.
Requests use the user or workspace context encoded by the pasted key. An Elite organization key
operates on organization resources, not a member's separate personal workspace.
## Generate a copy-safe snippet
Each expanded endpoint provides **curl**, **TypeScript**, and **Python** tabs. The generated code:
- references `$Y2_API_KEY`, `process.env.Y2_API_KEY`, or `os.environ["Y2_API_KEY"]`;
- never includes the key pasted into Workbench;
- substitutes an unset required path or query value with an `UPPER_SNAKE_CASE` environment
variable; and
- adds comments for the required and currently enabled query parameters, including available
descriptions, types, enums, and defaults.
For example, after enabling `limit` without entering a value, a curl snippet can contain:
```bash
# Required:
# countryCode (path) — ISO 3166-1 alpha-2 country code
#
# Optional:
# limit (query · integer · default: 8) — Maximum results
curl "https://api.y2.dev/api/v1/osint/countries/$COUNTRY_CODE/news?limit=$LIMIT" \
-H "Authorization: Bearer $Y2_API_KEY" \
-H "Accept: application/json"
```
Set the placeholders before running the copied command:
```bash
export Y2_API_KEY="y2_..."
export COUNTRY_CODE="UA"
export LIMIT="8"
```
Best for one-off checks and reproducing a request in a bug report. Unset values use shell
variables such as `$COUNTRY_CODE`.
Uses native `fetch` and reads secrets or missing values from `process.env`. Move the API key to
server-side configuration before integrating the snippet into an application.
Uses `httpx`, reads configuration from `os.environ`, calls `raise_for_status()`, and applies a
30-second client timeout.
## Use the AI Quickstart prompt
**Copy prompt** copies a short instruction that directs a coding agent to the canonical Agentic DX
and MCP guides before it integrates Y2. It contains documentation URLs and safety guidance, not the
API key entered on the page.
Use this prompt as orientation, then give the agent the exact endpoint, scope, and outcome you need.
Keep `Y2_API_KEY` in the agent runtime or MCP client environment—not in the prompt or tool arguments.
## Know the current boundaries
| Capability | Current behavior |
| --- | --- |
| API target | Production server from OpenAPI; falls back to `https://api.y2.dev/api/v1` |
| Path and query fields | Interactive and OpenAPI-driven |
| Request body fields | Not rendered; use endpoint reference examples |
| Live response | Status and response body |
| Response headers | Not displayed in the inline pane |
| Authentication | Bearer key required by the runner |
| x402 | Use curl or an x402 client outside Workbench |
| Saved state | Key, expanded rows, and parameter values are page component state, not durable presets |
Workbench is an integration aid, not a replacement for application-level error handling. Before
shipping copied code, add timeouts, typed response handling, pagination, retry policy, and secret
management appropriate to your runtime.
---
# Pay per Request with x402 (/docs/api/x402)
x402 lets an agent or application pay for a supported Y2 read request without creating an account
or API key. Y2 advertises an x402 v2 payment requirement, verifies and settles the signed payment,
then serves the normal endpoint response.
```mermaid
sequenceDiagram
participant C as Client
participant Y as Y2 API
participant F as Facilitator
C->>Y: Request without Authorization
Y-->>C: 402 + PAYMENT-REQUIRED
C->>C: Validate and sign requirement
C->>Y: Same request + PAYMENT-SIGNATURE
Y->>F: Verify payment
F-->>Y: Valid
Y->>F: Settle payment
F-->>Y: Transaction
Y->>Y: Run resource handler
Y-->>C: Resource response + PAYMENT-RESPONSE
```
## Choose bearer auth or x402
| Mode | Use it for |
| --- | --- |
| Bearer API key | Recurring integrations, workspace-scoped data, writes, and plan rate limits |
| x402 | Anonymous, one-off, agent-driven access to supported public read operations |
A request with `Authorization: Bearer ...` always takes the API-key path. An invalid bearer token
returns its normal `401` or `403`; Y2 does not silently fall back to x402. Omit `Authorization`
entirely when you intend to pay per request.
## Supported API surfaces
x402 is enabled on registered read operations in these families:
- Reports, including Markdown, text, signals, graph, audio, and TTS text;
- News items, recaps, and feed discovery;
- OSINT feeds and country intelligence;
- Intel v2 incidents, entities, markets, financial intelligence, signals, cyber, and changes.
It is not an alternative for API-key-only surfaces such as profile management, webhook
configuration, or Agent Y2. Check an operation's `security` and `x-x402` fields in the
[generated endpoint reference](/docs/api/reference) before building a payment flow.
Anonymous callers do not have a user or workspace context. For example, `GET /reports` requires
a canonical `profileId` query parameter on the x402 path.
## Read the payment challenge
Send the exact resource request without a bearer key:
```bash
curl --include \
"https://api.y2.dev/api/v1/news?topics=markets&limit=5"
```
The `402` response includes a base64-encoded `PAYMENT-REQUIRED` header and the decoded requirement
as JSON. Its shape is similar to:
```json
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "https://api.y2.dev/api/v1/news?topics=markets&limit=5",
"description": "Real-time direct-source news terminal items",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "2000",
"payTo": "0x...",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}
],
"extensions": {
"bazaar": { "discoverable": true }
}
}
```
Do not hard-code `asset`, `payTo`, EIP-712 domain fields, or the amount from this example. Read and
validate the returned requirement every time. `amount` is expressed in atomic token units; USDC
uses six decimals, so `"2000"` represents `$0.002`.
Production requirements use Base mainnet (`eip155:8453`). Development deployments can use Base
Sepolia (`eip155:84532`). The network is a CAIP-2 identifier, not the legacy `base` slug.
## Make a paid request in TypeScript
The following flow matches the x402 packages used by the current Y2 implementation:
```bash
npm install @x402/core@^2.18.0 @x402/evm@^2.18.0 viem@^2.55.2
```
Create the signer in a server-side runtime or secure wallet environment. Never expose a raw private
key in browser code.
```typescript
import { x402Client } from "@x402/core/client";
import { x402HTTPClient } from "@x402/core/http";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const privateKey = process.env.X402_PRIVATE_KEY as `0x${string}` | undefined;
if (!privateKey) throw new Error("X402_PRIVATE_KEY is required");
const signer = privateKeyToAccount(privateKey);
const core = new x402Client().register("eip155:*", new ExactEvmScheme(signer));
const x402 = new x402HTTPClient(core);
const url = "https://api.y2.dev/api/v1/news?topics=markets&limit=5";
const challengeResponse = await fetch(url, { headers: { Accept: "application/json" } });
if (challengeResponse.status !== 402) {
throw new Error(`Expected an x402 challenge, received ${challengeResponse.status}`);
}
const challengeBody = await challengeResponse.json();
const requirement = x402.getPaymentRequiredResponse(
(name) => challengeResponse.headers.get(name),
challengeBody,
);
const option = requirement.accepts[0];
if (!option || option.scheme !== "exact" || option.network !== "eip155:8453") {
throw new Error("Y2 returned an unsupported payment option");
}
const maxAtomicUsdc = 2_000n;
if (BigInt(option.amount) > maxAtomicUsdc) {
throw new Error("Payment exceeds the client price ceiling");
}
const payment = await x402.createPaymentPayload(requirement);
const paidResponse = await fetch(url, {
headers: {
Accept: "application/json",
...x402.encodePaymentSignatureHeader(payment),
},
});
const settlement = paidResponse.headers.has("PAYMENT-RESPONSE")
? x402.getPaymentSettleResponse((name) => paidResponse.headers.get(name))
: null;
const result = await paidResponse.json();
console.log({ status: paidResponse.status, settlement, result });
```
The price ceiling above is specific to the `$0.002` News example. Set a ceiling appropriate to the
operation you selected. Keep the first and second request URL, method, query, and body identical.
## Understand the headers
| Header | Direction | Meaning |
| --- | --- | --- |
| `PAYMENT-REQUIRED` | Response | Base64-encoded x402 v2 challenge |
| `PAYMENT-SIGNATURE` | Request | Base64-encoded signed payment payload |
| `PAYMENT-RESPONSE` | Response | Base64-encoded settlement result |
| `Authorization` | Request | Selects bearer authentication when present |
`X-PAYMENT` and `X-PAYMENT-RESPONSE` are legacy x402 v1 names. New Y2 clients should use the v2
headers above.
## Prices and wallet limits
Each operation maps to one current tier. The generated endpoint page is the source of truth for the
operation's exact price.
| Tier | Price | Per wallet/minute | Per wallet/day |
| --- | ---: | ---: | ---: |
| Health/meta | $0.001 | 60 | 10,000 |
| Standard read | $0.002 | 60 | 5,000 |
| Enriched data | $0.01 | 30 | 2,000 |
| Premium report list | $0.05 | 10 | 500 |
| Premium single report | $0.25 | 10 | 200 |
| Premium report audio | $0.50 | 5 | 100 |
Y2 also applies a global per-endpoint circuit breaker. A wallet limit returns `429`; an endpoint
capacity limit returns `503`. Both are checked before settlement, so the rejected request is not
charged.
## Store and inspect a receipt
The EIP-3009 authorization inside the signed payment contains a nonce. Treat that nonce as a receipt
lookup capability and store it with the request result:
```bash
curl "https://api.y2.dev/api/v1/x402/receipts/$PAYMENT_NONCE"
```
The lookup is public to anyone who knows the nonce. It returns the payer address, endpoint, USD
amount, network, status, and—when available—the transaction hash and settlement time. It does not
return the signature, token authorization, pay-to address, or full payment payload.
| Receipt status | Meaning |
| --- | --- |
| `verify_failed` | The facilitator rejected the payment |
| `rate_limited` | Wallet or endpoint limits blocked the request before settlement |
| `replay_blocked` | The payer and nonce combination was already recorded |
| `settle_failed` | Verification passed but settlement did not complete |
| `settled` | Payment settled and Y2 invoked the resource handler |
| `handler_failed` | Payment settled, then the resource handler threw |
A settled payment can still be followed by an application response such as `400`, `404`, or a
temporary data-readiness error. Validate resource IDs and parameters before signing, inspect the
paid response status and body, and retain the nonce and `PAYMENT-RESPONSE` metadata for support.
## Handle failures
| Status | Typical cause |
| --- | --- |
| `401` or `403` | Invalid bearer key or missing bearer scope; no x402 fallback |
| `402` | Missing or invalid payment, unsupported payment payload, or nonce replay |
| `429` | Per-wallet minute or daily limit exceeded |
| `503` | x402 unavailable, settlement infrastructure unavailable, or endpoint circuit breaker open |
| Other endpoint status | Payment settled, then the normal resource handler returned its own result |
Never reuse an authorization nonce. When a result is ambiguous, check the receipt before creating a
new payment so you do not confuse a settled request with a transport failure.
---
# Audio Reports (/docs/audio)
Audio Reports turn a generated intelligence report into an MP3 narration. The feature is
available in active Pro and Elite workspaces and is currently labeled **Alpha** in the profile
editor.
Audio is configured per profile. Enabling narration affects future report runs; it does not
generate audio for reports that already exist.
```mermaid
flowchart LR
A[Generate report] --> B[Prepare speech text]
B --> C[Apply branding and synced dictionary]
C --> D[Cartesia Sonic-3]
D --> E[MP3 in Convex storage]
E --> F[In-app player]
E --> G[Email link]
E --> H[API and webhook status]
```
## Enable narration for a profile
### Open the profile editor
Go to **InfoOps → My Profiles**, then create a profile or edit an existing one.
### Open Advanced
Continue to the **Advanced Configuration** step and find **Audio Narration**.
### Turn on audio
Enable the switch. Free and Lite workspaces cannot enable it; the backend also rejects an
enabled audio configuration when the active workspace lacks the entitlement.
### Choose the narration settings
Preview and select a voice, choose a generation speed, and optionally assign a synced
pronunciation dictionary. If no voice is selected, Y2 uses its default professional
narrator.
### Save the profile
Audio is generated the next time that profile produces a non-degraded report.
The voice library groups available voices into News & Broadcast, Professional, Narrative &
Documentary, and Casual & Conversational. The stored catalog can include English and Spanish
voices with masculine, feminine, or neutral labels. Availability is data-driven, so the exact
voice list can change.
The current dictionary selector lists all dictionaries in the active scope; it does not filter
them to the selected voice's language. Check the language badge yourself before assigning one.
## What happens during generation
Y2 prepares a speech-specific version of the report before calling the text-to-speech provider:
- It removes the trailing references section, citation markers, HTML, and Markdown formatting.
- It normalizes headings, punctuation, common entities, numbers, currency, acronyms, and tickers
for spoken output.
- It adds the assigned branding template's audio intro, before-report audio block, after-report
audio block, and outro. When no value is configured, Y2 branding defaults apply.
- It applies the assigned pronunciation dictionary only when that dictionary has a provider ID
and a `synced` status.
- It generates an MP3 with Cartesia Sonic-3 at 44.1 kHz and 128 kbps, then stores the file and its
metadata in Convex.
Audio output is limited to 20 MB. Generation is also skipped when its estimated cost would push
the report above a configured per-report budget.
Audio is an optional workflow step. A missing API key, provider error, oversize result, budget
limit, or degraded report can leave the report without audio. Report delivery continues when
the audio step returns no file.
The stored duration is an estimate based on the prepared text, not a measurement read from the
finished MP3. The in-app player replaces it with the browser-reported media duration after the
file loads.
## Create a pronunciation dictionary
Pronunciation dictionaries are also an Alpha feature. Pro workspaces can store up to 10 and Elite
workspaces up to 25. Each dictionary can contain up to 200 entries.
### Open Audio
Go to **InfoOps → Audio** and select **Create** under Pronunciation Dictionaries.
### Define the dictionary
Enter a name, optional description, and English or Spanish language. Add entries individually
or use the CSV bulk-import control.
### Add replacements
Each entry stores a source word, a pronunciation value, and an Alias or IPA label. The current
provider sync sends entries as text-to-alias replacements, regardless of the stored label, so
use a readable replacement string and test the generated result.
### Save and verify sync
Creating or editing a dictionary starts an automatic provider sync. Confirm that its badge
becomes **Synced**. If it becomes **Error**, inspect the displayed error and use the refresh
button to retry after correcting the entries.
### Assign it to a profile
Return to the profile's **Advanced Configuration**, enable Audio Narration, select the
dictionary, and save.
| Constraint | Enforced limit |
| --- | ---: |
| Dictionaries in Pro | 10 |
| Dictionaries in Elite | 25 |
| Entries per dictionary | 200 |
| Dictionary name | 50 characters |
| Description | 200 characters |
| Source word | 100 characters |
| Replacement | 300 characters |
Editing entries moves the dictionary back to `pending` before it is synced again. If a selected
dictionary is pending, errored, deleted, or outside the profile's tenant scope, Y2 does not apply
it during generation.
## Customize spoken branding
The **InfoOps → Audio** page also exposes the intro and outro for each branding template in the
active workspace. Use `{name}` to insert the profile name.
| Spoken content | Limit | Position |
| --- | ---: | --- |
| Audio intro | 100 characters | Before all other narration |
| Before-report audio | 200 characters | After the intro and optional label |
| After-report audio | 200 characters | Before the outro and optional label |
| Audio outro | 200 characters | At the end |
The before- and after-report audio fields live in the full
[Branding Templates editor](/docs/profiles/branding). A template must be explicitly assigned to
the profile for its custom values to apply.
## Play and deliver audio
When audio is available, the report page displays **Listen**. The bottom-sheet player supports
play and pause, seeking, download, and playback rates of 0.75x, 1x, 1.25x, 1.5x, and 2x. Voice
and duration metadata are displayed when available.
Delivery surfaces expose audio differently:
| Surface | Current behavior |
| --- | --- |
| In-app report | Shows the player only when the report has an audio URL |
| Email | Shows an “Audio Narration Available” callout linking to the report page; the MP3 is not attached |
| Webhook | Reports `available` or `unavailable`, an estimated duration, and an API audio link |
| Reports API | Returns audio metadata or redirects to the stored MP3 URL |
Audio files are removed when their owning report, profile, workspace, or account is deleted and
can also be affected by storage cleanup. Do not treat a returned storage URL as a permanent
archive.
## Retrieve audio through the API
Use an API key with the `reports:audio` scope, or complete the endpoint's x402 flow. The path uses
the report's public API ID.
```bash
curl --request GET \
--url "https://api.y2.dev/api/v1/reports/REPORT_ID/audio" \
--header "Authorization: Bearer YOUR_API_KEY"
```
```json
{
"data": {
"url": "https://example.convex.cloud/api/storage/…",
"duration": 847,
"durationSeconds": 847,
"durationFormatted": "00:14:07",
"format": "mp3",
"mimeType": "audio/mpeg",
"fileSizeBytes": 13420800
}
}
```
`duration` is retained for compatibility and is deprecated; use `durationSeconds`. Add
`?redirect=true`, or request an audio media type with the `Accept` header, for a `302` response to
the stored file URL.
The related `GET /api/v1/reports/{reportId}/audio-text` endpoint requires `reports:read` and
returns the speech-preprocessed text, character count, and estimated duration without requiring
that an MP3 already exists. See the generated [Reports reference](/docs/api/reference/reports) for
canonical schemas, errors, rate limits, and x402 pricing.
## Troubleshoot audio
Confirm that the active workspace—not another personal or team workspace—is Pro or Elite.
Free and Lite workspaces do not have the audio entitlement.
The button appears only after audio metadata is stored. Confirm that narration was enabled
before the run. Provider configuration, a degraded report, the 20 MB limit, or a per-report
budget can cause audio to be skipped without blocking report delivery.
Confirm that the profile has the intended dictionary selected and that the dictionary badge
says Synced. Check the dictionary and voice language manually because the current selector
does not enforce a match.
Generation stores an estimate based on text length. The player reads the MP3's actual duration
from browser media metadata after loading, so the value can change.
Verify the public report ID, report access, and that audio exists for the report. The endpoint
uses the same not-found response when no audio is available.
## Next steps
Configure the report workflow that produces narration.
Add intros, outros, and channel-specific static content.
Retrieve reports and their derived representations programmatically.
Compare audio access and pronunciation-dictionary quotas.
---
# Create an Automation (/docs/automations/creating)
This tutorial creates an Automation, activates it, and verifies its first run.
## Before you begin
You need a Pro or Elite active workspace and a write-capable workspace role. If the Automation
will use a Project or profile trigger, create that resource in the same workspace first.
## 1. Open Automations
Open **Automations** from the app navigation. If you arrived from a Project, the form can start
with that Project selected.
## 2. Define the work
Select **New Automation**, enter a name, and write the instruction Agent Y2 should execute. Keep
the instruction focused on one repeatable outcome. The instruction can contain up to 8,000
characters.
## 3. Choose the context
Optionally select a Project. Y2 accepts only an active Project owned by you in the active
workspace. Leaving Project blank creates a workspace-scoped output thread.
## 4. Choose a trigger
For a schedule, choose daily, weekly, or monthly, then set the local time and timezone. Weekly
schedules also require a weekday; monthly schedules require a day from 1 through 28.
For a report trigger, select an active InfoOps profile from the same owner and workspace. The
Automation runs after a completed report event is accepted.
## 5. Choose notifications and status
Choose notifications for all runs, failures only, or none. Save as **Draft** while reviewing the
definition, or save as **Active** to accept triggers. Active schedule definitions receive their
next scheduled run.
## 6. Verify the Automation
Use **Run now** on an active definition. Review run history for queued, running, succeeded,
failed, skipped, or canceled status, then open the output thread when it is available.
Y2 skips overlapping runs, stale definition revisions, lost plan or membership access, and
inaccessible Projects. A skipped run does not bypass workspace or owner checks.
---
# Automations (/docs/automations)
Automations run a bounded Agent Y2 instruction without requiring someone to open Chat. An
Automation can run on a schedule or after a selected InfoOps profile finishes a report, and it can
optionally keep its output inside a Project.
Automations are not included with Free or Lite. Pro workspaces can keep up to 10 active, paused,
or draft definitions; Elite workspaces can keep up to 50. The limit is shared by the workspace.
```mermaid
flowchart LR
trigger[Schedule or report event] --> admission[Workspace and entitlement check]
admission --> run[Agent Y2 run]
run --> thread[Workspace or Project output thread]
run --> history[Run history and notification]
```
## Definition and run separation
The Automation definition stores its name, instruction, trigger, notification policy, optional
Project, and current revision. Every execution creates a separate run snapshot. Editing a
definition does not rewrite prior run history.
Only one run for a definition can execute at a time. When a trigger arrives while another run is
active, the newer run is skipped instead of overlapping.
## Trigger types
| Trigger | Behavior |
|---|---|
| Schedule | Daily, weekly, or monthly in the selected IANA timezone. |
| Profile report completed | Runs once for each accepted completion event from the selected active profile. |
| Manual | **Run now** creates an immediate run from an active definition. |
Profile triggers must reference a profile owned by the same user in the same workspace. A
Project-bound Automation must also reference an active Project with the same owner and workspace.
## Access changes and downgrade safety
Y2 checks current workspace membership, plan access, definition revision, and Project access again
when a run starts. If Automations are no longer available, Y2 skips the run, pauses the definition,
and cancels its schedule. A downgraded owner can still pause or archive an existing definition so
cleanup is not blocked by the upgrade gate.
## Cost and output
Each accepted run reserves a bounded Agent Y2 budget, then settles actual model and tool costs or
refunds the reservation after a failed or skipped run. Output is written to the definition's
workspace-bound thread. Project-bound output stays in that Project.
## Elite governance
Elite audit logs bind Automation creation, edits, status changes, and manual run requests to the
acting user and workspace. Run records also retain the owner, workspace, trigger, definition
revision, billing state, and output identifiers.
## Choose the next task
Configure a trigger, instruction, Project, and notification policy.
Review limits, statuses, triggers, run states, and access checks.
Build the private objective and context an Automation can operate within.
---
# Automations Reference (/docs/automations/reference)
## Availability and limits
| Plan | Access | Shared workspace definition limit |
|---|---|---:|
| Free | No | 0 |
| Lite | No | 0 |
| Pro | Yes | 10 |
| Elite | Yes | 50 |
Draft, active, and paused definitions count toward the limit. Archived definitions do not.
Definitions are owner-private even in an Elite organization.
## Definition fields
| Field | Constraint or value |
|---|---|
| Name | Required, up to 100 characters. |
| Instructions | Required, up to 8,000 characters. |
| Operation | `agent_y2` |
| Overlap policy | `skip_if_running` |
| Status | Draft, active, paused, or archived. |
| Notification policy | All runs, failures only, or none. |
| Project | Optional active Project with the same owner and workspace. |
## Schedule triggers
| Frequency | Required fields |
|---|---|
| Daily | Time of day and IANA timezone. |
| Weekly | Time of day, timezone, and weekday. |
| Monthly | Time of day, timezone, and day of month. |
## Run states
`queued` → `running` → `succeeded` or `failed` is the normal path. A run can instead become
`skipped` when admission checks fail or another run overlaps, or `canceled` when its work is
explicitly stopped.
Billing states are `reserved`, `settled`, `refunded`, and `none`. Each run snapshots the
definition revision, instruction, name, trigger, workspace, optional Project, and cost metadata.
## Live admission checks
Before paid work starts, Y2 verifies:
- The definition is active and still at the queued revision.
- The owner is still a member of the definition workspace.
- The workspace still has Pro or Elite Automation access and Chat capacity.
- A bound Project is active, owner-matched, workspace-matched, and entitled.
- No earlier run for the definition is currently active.
These checks apply to schedules, report events, manual runs, and workflow continuation.
---
# March 2026 Release Archive (/docs/changelog/2026-03)
[← Back to the latest release](/docs/changelog)
## March 2026: Y2SOURCE Event Provenance Engine
This entry describes the March 2026 launch. At launch, Y2SOURCE was available on **Lite** and
**Pro**. See [Plans and limits](/docs/plans) and the [OSINT API guide](/docs/api/osint) for
current access and request behavior.
Y2 intelligence reports now feed the Situation Room. The **Y2SOURCE extraction pipeline** extracts
structured geopolitical events from reports generated by the Y2 Orchestration Engine. Each event
includes provenance metadata linking it to the source report.
### What changed
#### Event extraction from reports
- Every Y2 intelligence report is now processed through a structured extraction pipeline that
identifies discrete geopolitical events, including conflicts, political shifts, economic
signals, and cyber incidents.
- Extracted events include geographic coordinates, severity, and temporal analysis such as
`ongoing`, `past`, `forecast`, or `analysis`.
- Named-entity recognition captures people, organizations, countries, and locations referenced in
each event.
#### Situation Room integration
- Y2-sourced events appear on the interactive globe alongside external OSINT feeds.
- Y2 markers distinguish report-derived intelligence from direct provider observations.
- Standard map filters such as category, severity, and region apply to Y2 events.
#### Provenance metadata
- Each Y2 event carries provenance metadata that connects the observation to report-derived
extraction context.
- Confidence scores from 0 to 1 reflect the extraction model's certainty about each event.
- Temporal classification indicates whether an event is ongoing, historical, forecast, or
analytical.
### Query report-extracted events
`GET /api/v1/osint/y2-events` returns only observations with `sourceType: "y2_report"`. It accepts
`category`, `severity`, `countryCode`, `limit`, and `cursor`; Bearer-token requests require the
`osint:read` scope.
```bash
curl -H "Authorization: Bearer y2_your_key" \
"https://api.y2.dev/api/v1/osint/y2-events?category=conflict&severity=high&limit=10"
```
The current JSON response uses stable public IDs and cursor pagination:
```json
{
"data": [
{
"id": "obs_7Jf2kQ9mV4sP",
"type": "observation",
"sourceType": "y2_report",
"title": "Escalation in Eastern Mediterranean Naval Activity",
"description": "Multiple carrier groups repositioned...",
"category": "conflict",
"severity": "high",
"coordinates": { "lat": 34.5, "lon": 33.0 },
"geometry": { "type": "Point", "coordinates": [33.0, 34.5] },
"locationName": "Eastern Mediterranean",
"countryCode": "CY",
"region": "middle_east",
"eventTime": 1784023200000,
"eventTimeISO": "2026-07-14T10:00:00.000Z",
"occurredAt": "2026-07-14T10:00:00.000Z",
"observedAt": null,
"url": "https://example.com/source",
"provenance": {
"source": "y2_report",
"confidence": 0.92
}
}
],
"meta": {
"pageCount": 1,
"limit": 10,
"hasMore": false,
"nextCursor": null
},
"links": {
"self": "/api/v1/osint/y2-events?category=conflict&severity=high&limit=10",
"next": null
}
}
```
See the generated [`GET /osint/y2-events` reference](/docs/api/reference/osint/listY2Events) for
all parameters, representations, and error responses.
### How the pipeline works
```mermaid
flowchart LR
report["Generate report"] --> extract["Extract events"]
extract --> normalize["Normalize places and entities"]
normalize --> observe["Upsert observations"]
observe --> resolve["Link provenance and incidents"]
```
The pipeline identifies structured events, resolves locations and entities, and upserts each event
as an observation with `sourceType: "y2_report"`. Internal lineage retains the report, model, and
confidence context used during extraction.
Y2-sourced observations have a 14-day time-to-live. Expired rows are excluded from API and app
queries and removed by OSINT maintenance.
---
## Profile sharing defaults
Community profile sharing is now **enabled by default** when creating new profiles. You can still
turn sharing off in profile settings.
---
## Situation Room map enhancements
- Y2 report-sourced events now render with distinct Y2-branded markers on the globe.
- Map-layer rendering for Y2 provenance events was improved.
---
# June 2026 Release Archive (/docs/changelog/2026-06)
[← Back to the latest release](/docs/changelog)
This archive preserves what changed in June 2026. Follow its links to feature and API guides for
current access, limits, and request contracts.
## June 30, 2026: News Terminal, Lite trial signup, and platform reliability
Y2 now includes the rebuilt News Terminal, a simpler Lite trial signup path, and reliability
improvements across billing, chat, delivery, developer tools, and intelligence data quality.
### What changed
#### News Terminal
- The News Terminal now uses direct-source feeds and appears throughout the product as the Y2
News Terminal / Signal Feed, replacing legacy Gloria-branded copy in active customer surfaces.
- Users can select source feeds, switch between Live Feed and 12h Recap views, refresh the active
view, and keep feed/view preferences across reloads.
- Recaps now summarize the same signals shown in the live feed, keeping summaries aligned with
the terminal while reducing unnecessary source refreshes.
- News API and reference docs now describe the Y2 News Terminal consistently, with clearer
responses for invalid topics, invalid timeframes, and feeds that are still initializing.
#### Lite trial signup and billing
- New signup now guides users toward a Lite trial by default while keeping Free available as a
fallback path.
- Lite checkout starts a 7-day trial and returns users to briefing setup once subscription access
is confirmed.
- If checkout finishes before subscription access has synced, users now see a pending setup state
with actions to check again or return to plan selection instead of being redirected prematurely.
- Trialing subscriptions now unlock paid access consistently across briefing setup, chat, usage
tracking, and billing views.
- Billing views now show clearer workspace subscription status, owner-only billing controls, and
profile reclaim notices after plan changes.
#### App, Chat, and delivery reliability
- App shell navigation and scrolling are more stable on mobile and desktop.
- Chat, profile setup, report delivery, audio, SMS, and webhook workflows received reliability
improvements around usage tracking, delivery state, and customer-facing errors.
- Report and briefing layouts now handle long reports, delivery preferences, branding, and profile
setup more clearly.
#### Developer and intelligence workflows
- API Workbench endpoints are now grouped into collapsible categories, making larger API surfaces
easier to scan.
- Generated API examples keep pasted API keys out of copied snippets, and the workbench includes
a copyable AI Quickstart prompt for teams integrating Y2.
- Aircraft tracks and normalized entity identifiers now feed the intelligence graph more
consistently, reducing duplicate entities and improving relationship context across OSINT and
Explorer views.
---
## June 18, 2026: Chat reliability, OSINT Monitor, and landing performance
Y2 now hardens chat image generation, streamlines the OSINT monitor workspace, and adds
landing-page performance guardrails for responsive marketing assets.
### What changed
#### Chat and configuration reliability
- Image generation now reserves chat credits before external model calls, refunds reserved credits
on provider, upload, or storage failures, and avoids double charging when generated images are
saved.
- Chat stream error handling now returns a stable `INTERNAL_ERROR` code and logs redacted identity
metadata instead of exposing raw token or subject values.
- Convex client setup and chat HTTP URL derivation now fail fast when `VITE_CONVEX_URL` is missing.
- Open Graph report routes derive public URLs from `SITE_URL`, with `https://y2.dev` as the
production fallback.
- Chat web-search source chips now ignore malformed or unsupported URLs instead of throwing during
render.
#### OSINT Monitor experience
- The monitor bottom bar now uses four resizable panes: live video, theater headlines, FININT, and
intel findings.
- CNBC is promoted to the first live-news channel, and the removed duplicate news terminal gives
more room to the analysis panels.
- Lite workspaces now unlock the full Situation Room experience: live tracking, naval vessels,
GPS interference, markets, prediction signals, anomaly/convergence/cascade analysis, exports,
and the full paid OSINT limits.
- The monitor layer sidebar now treats every Situation Room layer as Lite-accessible, so Lite users
can enable the same map layers available to Pro and Elite workspaces.
#### Landing-page performance
- Walkthrough images now use versioned responsive `srcSet` metadata, intrinsic dimensions, eager
first-image loading, and lower-priority lazy loading for later images.
- New 960px marketing image derivatives reduce walkthrough screenshot payloads, with immutable
Netlify caching for `/images/marketing/*`.
- `bun run check:landing-perf` validates stale hero preloads were removed, marketing images stay
versioned and responsive, and responsive derivatives remain smaller than their originals.
---
## June 2026: Y2 Elite B2B, Organization Workspaces and Intel API v2
Y2 Elite is live as the flat-priced B2B organization plan. Elite is priced at **$500/mo flat** or
**$5,000/yr** and includes one organization workspace with up to **5 members**, shared governance,
audit logs, the highest API/profile/workspace limits, and expanded Intel API access.
Y2 Workspaces now support the current organization model for companies, teams, and departments.
Personal Free, Lite, and Pro workspaces continue to serve individual workflows; Elite creates a
shared organization workspace where profiles, reports, API keys, usage, delivery settings, and
governance resolve from the active workspace.
```mermaid
flowchart LR
people["Owner and members"] --> workspace["Active organization workspace"]
workspace --> intel["Profiles and reports"]
workspace --> delivery["Delivery and branding"]
workspace --> api["API keys and usage"]
workspace --> governance["Roles and audit logs"]
```
This release also expands the Intel API v2 surface served from `https://api.y2.dev/api/v2`. The new
Intel surface is gated by `intel:finint`, `intel:cyber`, and `intel:explorer` scopes. As of July 21,
2026, new API-key access to those scopes requires Pro or Elite. Existing Lite
API users retain their keys at historical Lite limits; Lite retains in-app
FININT/Cyber/Explorer, and the in-app Cyber dashboard remains available on Free.
### What changed
#### Organization workspaces
- Elite creates a shared B2B workspace for organizations, teams, and departments.
- Owners and admins can invite members, manage roles, and review workspace governance.
- Workspace resources are loaded and protected server-side, including profiles, subscriptions,
reports, audio, branding templates, delivery settings, API keys, and usage.
- API keys belong to the active workspace and enforce workspace-scoped resource access, scopes,
IP allowlists, and aggregate rate limits.
#### Elite pricing and limits
| Feature | Elite |
|---------|-------|
| Price | $500/mo flat or $5,000/yr |
| Members | Up to 5 per organization workspace |
| Profiles | 50 total custom and community profiles |
| Branding Templates | 25 |
| API Keys | 25 |
| API Rate Limits | 120 req/min, 50,000 req/day |
| Chat Budget | 5,000 credits/month |
| Situation Room | 50 layers, 100 alert rules, 50 watchlist countries |
| OSINT Refresh & Retention | 1 min refresh, 90 day snapshot retention |
| Pronunciation Dictionaries | 25 |
Elite includes 10x Pro daily API capacity, 5x Pro API-key capacity, 5x Pro chat budget, 2.5x Pro
profile capacity, and organization-only governance.
#### B2B governance and safety
- Workspace members and audit logs are available on Elite organization workspaces.
- Team usage summaries and API-key activity are available from workspace governance views.
- Invite acceptance activates the paid organization workspace while preserving each user's
separate personal Free workspace.
- Free-trial report limits remain user-global, so creating additional workspaces does not mint new
trial capacity.
#### Intel API v2
The new Intel API exposes Y2's ontology layer through HTTP endpoints: deduplicated incidents,
canonical entities, entity-relationship graphs, prediction and Y2 markets, emergent signals, and
financial intelligence. It serves the same data as the in-app Explorer, FinInt, and Cyber sections.
The 11 endpoints shipped in this launch accept both API keys and
[x402 pay-per-request](/docs/api/x402):
The additive `GET /api/v2/changes` feed was released later. The current surface therefore has 12
paths; use the [Intel API guide](/docs/api/intel) and generated reference for the live inventory.
| Method | Endpoint | Tier | Scope |
|--------|----------|------|-------|
| `GET` | `/api/v2/incidents` | T1 ($0.001) | `intel:explorer` (or `finint`/`cyber` by category) |
| `GET` | `/api/v2/incidents/{id}` | T2 ($0.002) | scope resolved from incident category |
| `GET` | `/api/v2/entities` | T1 ($0.001) | `intel:explorer` |
| `GET` | `/api/v2/entities/{id}` | T2 ($0.002) | `intel:explorer` |
| `GET` | `/api/v2/entities/{id}/graph` | T3 ($0.01) | `intel:explorer` |
| `GET` | `/api/v2/markets` | T1 ($0.001) | `intel:finint` |
| `GET` | `/api/v2/finint` | T1 ($0.001) | `intel:finint` |
| `GET` | `/api/v2/signals` | T3 ($0.01) | `intel:finint`, `intel:cyber`, or `intel:explorer` by domain |
| `GET` | `/api/v2/cyber/graph` | T3 ($0.01) | `intel:cyber` |
| `GET` | `/api/v2/cyber/cves` | T1 ($0.001) | `intel:cyber` |
| `GET` | `/api/v2/cyber/actors` | T1 ($0.001) | `intel:cyber` |
New Intel scopes:
- `intel:finint` — markets, finint indicators, non-cyber incidents, market/supply-chain signals
- `intel:cyber` — CVEs, threat actors, malware families, cyber graph, cyber incidents,
cyber/technology signals
- `intel:explorer` — universal entity catalog, entity drill, entity graph, any-category incidents,
broad signal queries
Every v2 response stamps `X-Y2-API-Version: 2.0` so programmatic clients can branch on surface. The
in-app [API Workbench](/app/developers/api-workbench) lists Intel endpoints in a dedicated Intel
group, the full reference lives in [Intel API documentation](/docs/api/intel), and the OpenAPI spec
at `/api/openapi.yaml` includes all 11 v2 paths with operation-level `servers:` overrides.
#### Everything in Pro, plus team scale
Elite includes Pro capabilities such as full Situation Room access, audio narration, SMS and
webhook delivery, scheduled reports, advanced research, FININT/Cyber/Explorer API scopes,
investigation sharing, live/vessel/market tracking, anomaly/convergence/cascade analysis, and data
export.
```bash
curl -H "Authorization: Bearer $Y2_API_KEY" \
"https://api.y2.dev/api/v2/incidents?category=cyber&severity=high&limit=10"
```
---
# July 2026 Release Archive (/docs/changelog/2026-07)
[← Back to the latest release](/docs/changelog)
This archive preserves what changed in July 2026. Follow its links to the maintained
feature and API guides for current behavior.
## July 30, 2026: Privacy, product platform, and installed-app foundation
Y2 strengthens its privacy terms, simplifies product analytics and feedback, and lays the
foundation for installed desktop and Android applications.
### Clearer data-processing terms
- The [Data Processing Agreement](/dpa) now defines customer and Y2 processing roles, United States
service-provider restrictions, security and breach duties, deletion and audit support, and
international transfer safeguards.
- New processing, subprocessor, and security schedules explain the data involved, each provider's
purpose, and Y2's technical and organizational safeguards.
- Customers now receive at least 30 days' notice before a new subprocessor materially processes
Customer Personal Data.
### One consent-controlled product platform
- Y2 removed Statsig and UserJot and consolidated product analytics, masked session replay, surveys,
and in-app Support in PostHog.
- Optional browser analytics remain consent-controlled. Session replay masks visible text and form
inputs, and reviewed product events exclude user-authored report, profile, and investigation
content.
- The Privacy Policy, DPA subprocessor schedule, and internal data-privacy audit now describe the
same current processing.
### Platform and installed-app foundation
- The web application and backend now use a shared Bun and Turborepo workspace, with common
translation and desktop contracts.
- Electron and Android shells add secure renderer boundaries, scoped device permissions, update and
packaging checks, and shared application behavior. Native signing and store distribution remain
separate release gates.
---
## July 24, 2026: Projects, Automations, and workspace governance
Y2 added private Project workspaces and durable Agent Y2 Automations with plan-aware access,
workspace isolation, and Elite governance.
### Projects on Lite, Pro, and Elite
- **Projects** organize chats, reports, profiles, graph boards, generated images, evidence, and
private documents around one objective.
- Graph, Timeline, Situation, Evidence, Knowledge, and Overview views keep related intelligence
connected without publishing Project content to Global Knowledge.
- Projects are bound to the active workspace and their owner. Switching workspaces cannot expose
another workspace's Project chats, resources, evidence, or private knowledge.
- Project sharing was not included in this release; Elite members did not automatically gain access
to another member's private Projects.
Follow the [Projects overview](/docs/projects) or
[create your first Project](/docs/projects/getting-started).
### Automations on Pro and Elite
- **Automations** run bounded Agent Y2 instructions daily, weekly, monthly, or after a selected
InfoOps profile completes a report.
- Pro includes up to **10** Automation definitions and Elite includes up to **50**, shared across
the active workspace. Free and Lite do not include Automations.
- An Automation can write to a workspace-bound output thread or stay inside an active Project.
- Live admission checks stop stale, overlapping, cross-workspace, downgraded, or inaccessible
Project runs before paid work continues.
- Run history records trigger, definition revision, output, status, and billing settlement; owners
can safely pause or archive definitions after a downgrade.
See [Automations](/docs/automations) and the
[Automation reference](/docs/automations/reference).
### Workspace governance and language support
- Elite audit records bind material Project and Automation changes to the acting user and
workspace without changing owner-private content access.
- Project and Automation navigation, plan guidance, upgrade states, forms, run history, and landing
explanations are available in English and Spanish.
- Plan, workspace, report-workflow, and profile documentation reflects the same enforced
entitlement boundaries.
---
## July 21, 2026: Global coverage, Chat updates, and Pro API keys
Y2 expanded global intelligence coverage, simplified active research, and tightened API-key
access.
### Intelligence
- New Situation Room visitors start with all **27 map layers** enabled. Existing preferences stay
unchanged, and mobile markers are easier to select.
- Discover adds monthly digests for **72 cities** and **39 industries**. Chat and onboarding now
recommend a matching public digest before creating a duplicate custom brief.
- News Terminal expands to **40 global topics** across geopolitics, markets, industries, regions,
AI, and crypto. High-signal items join Y2's ontology with deduplication, entity links, and source
provenance.
- Incident enrichment and corroboration now run together, with automatic recovery for interrupted
work and protection from stale retries.
Explore [News Terminal](/docs/news), [OSINT sources](/docs/osint/sources), or the
[News API](/docs/api/news).
### Chat
- New chats default to **GLM 5.2** using Zero Data Retention routing. Existing threads and saved
model choices stay unchanged.
- Featured models are now Nemotron 3 Ultra, Grok 4.5, GLM 5.2, DeepSeek V4 Pro, Kimi K3, Qwen 3.6,
GPT-5.6 Luna, Claude Sonnet 5, Gemini 3.6 Flash, and Mistral Small.
- Tool activity now uses a compact, auto-scrolling panel with clearer progress and failure states.
See [Chat Features](/docs/chat) for model selection and privacy details.
### API access
- New API keys require **Pro** (5 keys, 30 requests/minute, 5,000/day) or **Elite** (25 keys, 120
requests/minute, 50,000/day).
- Existing Lite keys keep their historical limits (10 requests/minute, 500/day) and can still be
rotated or revoked. Lite cannot create more keys, and future downgrades are not grandfathered.
Webhooks remain available on Lite, and x402 access is unchanged.
See [API Authentication](/docs/api/authentication) or
[x402 pay-per-request access](/docs/api/x402).
---
## July 17, 2026: Y2 Affiliate program
Every Y2 user can review the Affiliate Program in **Settings → Referrals**. A personal
FirstPromoter referral link is created and displayed only after the user explicitly accepts the
current program terms. The enrolled dashboard keeps provider credentials and raw customer details
server-side.
- The default Y2 Affiliate Program offers 15% recurring commission on eligible paid Lite and Pro
subscription revenue.
- Referral attribution converts only after a new customer completes a verified non-zero Lite or Pro
payment; a signup, 7-day trial, Free or Elite purchase, or fully discounted invoice does not
create a commissionable referral.
- A promoter must maintain an active paid Lite, Pro, or Elite workspace and keep their own Y2
payments current throughout commission eligibility, review, and payout.
- Pending commissions remain subject to the 60-day payment, refund, fraud, and eligibility review.
See the [Affiliate Program guide](/docs/affiliate-program) for enrollment, attribution, payout,
and troubleshooting details.
---
## July 10, 2026: Privacy-first Chat and connected intelligence
Y2 makes model selection clearer, keeps provider routing privacy-first, and connects Situation
Room signals to the wider intelligence workspace.
### What changed
#### Privacy-first Chat
- Y2 Chat and Agent Y2 route model requests through Zero Data Retention (ZDR) endpoints, with
provider data collection disabled. See [Chat Features](/docs/chat) and the
[Agent Y2 API guide](/docs/api/agent-y2).
- The model picker now shows readable names, provider filters, and search.
- GPT-5.6 Luna and NVIDIA Nemotron 3 Ultra are featured chat options, subject to current provider
availability.
#### Situation Room and connected intelligence
- The map now groups orbital and satellite layers, surveillance sources, live media, and expanded
infrastructure and military overlays in shared layer controls and a source catalog.
- The monitor tool rail places Intel Findings, Network Intel, ontology Search, and API Query beside
the map and loads each tool on demand.
- Live malware monitoring now includes relationship graphs for indicators, malware families, and
related entities.
- Search the Y2 ontology directly from the Situation Room to explore entities and their live
relationships.
- Map marker graphs now show richer source, confidence, time-range, and report context, with a
direct link to the related historical report when available.
- Monitor controls and data panels now adapt to smaller screens and simplify navigation.
#### Reliable scheduled reports
- Scheduled report generation now detects interrupted runs, queues bounded recovery attempts, and
keeps the profile active instead of delivering incomplete reports.
- Profile status and retry state are clearer when a report needs another attempt. See
[Scheduling](/docs/profiles/scheduling).
#### Clearer plan guidance
- Plan and branding documentation now matches current access: the full Situation Room is available
on Lite, Pro, and Elite, while reusable report branding is available on Pro and Elite. See
[Whitelabel Branding](/docs/profiles/branding).
---
## July 8, 2026: Agent Y2 API, OpenAI-compatible Chat, and MCP
Y2 now exposes Agent Y2 through API-key authenticated streaming, an OpenAI-compatible
chat-completions route, a standalone MCP server for agent clients, and refreshed OpenAPI,
client-integration, and endpoint documentation.
```mermaid
flowchart LR
client["Agent client"] --> native["Agent Y2 stream"]
client --> compatible["OpenAI-compatible stream"]
native --> executor["Shared Agent Y2 executor"]
compatible --> executor
executor --> tools["Scoped Y2 tools"]
```
### What changed
#### Agent Y2 API and OpenAI-compatible Chat
- Agent Y2 is now available through API-key authenticated streaming at
`POST /api/v1/agent-y2/chat/stream`; see the [Agent Y2 API guide](/docs/api/agent-y2)
and [generated Agent Y2 reference](/docs/api/reference/agent-y2).
- The OpenAI-compatible `POST /api/v1/chat/completions` route supports
`model: "y2-agent"` for streaming clients that already consume OpenAI-style chat
completion chunks.
- Both routes require the `agent:y2` API-key scope, use workspace-aware entitlements and
chat credit budgets, and attribute usage back to the correct workspace, thread, endpoint,
and API key.
- Agent Y2 API access is intentionally API-key only in this release, with a shared
`5/min`, `100/day` per-key throttle and a `10/min`, `250/day` workspace/user throttle
across both Agent Y2 API routes.
- OpenAI-compatible stateless chat calls preserve caller-provided transcript context when no
Y2 thread is attached, while threaded calls validate ownership before reserving chat
credits.
#### MCP, OpenAPI, and client documentation
`@y2-intel/mcp` is distributed separately from this platform repository. The maintained
[MCP guide](/docs/api/mcp) explains that boundary and shows how to inspect the capabilities of
the installed package version.
- New [Y2 MCP docs](/docs/api/mcp) cover the standalone `@y2-intel/mcp` stdio server for
Claude, Codex, Claude Desktop, and other MCP-compatible clients.
- The MCP server exposes Y2 docs and OpenAPI resources, bounded report and news tools, an
operation lookup tool, and `y2_ask_agent` for Agent Y2 access.
- The [OpenAPI specification](/docs/api/openapi), [Endpoint Reference](/docs/api/reference),
and [client guides](/docs/sdks) now reflect the canonical Agent Y2 routes, `agent:y2` scope,
request schemas, response codes, generated examples, and rate-limit headers.
- The [API Workbench](/docs/api/workbench) and docs navigation received sidebar, category,
route, theme-state, and 404 handling improvements for the expanded API surface.
#### Agent reliability and synthesis safety
- The shared Agent Y2 executor now powers both app-authenticated and API-key routes, keeping
the fixed Agent Y2 system prompt protected from caller override.
- Agent Y2 API responses hide internal reasoning chunks and align SDK examples to the
canonical `POST /api/v1/chat/completions` path.
- Degraded Y2 synthesis now fails closed instead of completing reports from low-confidence
output, with debugging documentation added for report-generation degradation triage.
---
# August 2026 Release Archive (/docs/changelog/2026-08)
[← Back to the latest release](/docs/changelog)
This archive preserves what changed in August 2026. Follow its links to the maintained feature
and API guides for current behavior.
## August 7, 2026: Reliable API results, safer access, and updated plan limits
Y2 makes API results more complete and predictable, strengthens access checks around Automations,
and updates profile limits across Free, Lite, and Pro.
### More reliable API results
- Filtered list endpoints now continue through their underlying data until they can return a full
matching page or reach the end, preventing false-empty and incomplete pages.
- API errors use stable public codes and messages without exposing internal Convex details.
- Response validation and the API contract now agree across reports, projects, Automations,
profiles, webhooks, and Situation Room data.
- API Workbench responses preserve documented JSON, text, and audio behavior more consistently.
See the [API overview](/docs/api), [OpenAPI guide](/docs/api/openapi), and
[API Workbench](/docs/api/workbench).
### Safer Automations
- Automation runs revalidate workspace write access before reserving or spending credits. If access
changes during a run, Y2 stops the work and settles the reservation safely.
- API authorization failures now return the same status and error shape across Project and
Automation routes.
### Updated profile limits and Lite trial
- Pro supports up to **5 custom profiles**, up to **20 community or global subscriptions**, and a
**20-profile combined workspace total**.
- Lite continues to include **1 custom profile** and **3 community subscriptions**, does not include
reusable branding templates, and starts with a **7-day trial** during onboarding.
- Free supports **1 global or community profile total**. Elite limits and pricing are unchanged.
See [Plans and Pricing](/docs/plans) and [Creating Profiles](/docs/profiles/creating).
---
## August 5, 2026: Connected Explorer, Documents, and Project Evidence
Y2 makes private documents easier to organize, automatically connects report sources to Project
Evidence, and opens entity exploration with useful relationship context.
### Connected Explorer neighborhoods
- Opening an entity in Ad-hoc Explorer now loads a focused neighborhood of related entities and the
connections between them instead of leaving an isolated node.
- Selecting another entity expands around it while preserving the temporary graph, making it easier
to compare related intelligence before adding it to a Project.
- Y2 Copilot now works from the active temporary graph and selected entity, keeping its analysis
grounded in what is visible without treating the session as a saved Project.
- Clear loading and retry states distinguish a relationship lookup from an entity with no known
connections.
### Broader private document support
- Documents now supports PDFs, word-processing files, spreadsheets, presentations, ebooks, and
common text formats for private knowledge.
- PDFs retain page structure for more precise retrieval, while files that cannot be read reliably
return a clear processing status instead of incomplete content.
- A simplified Documents library shows where each file is available, its current status, and the
actions needed to manage or restore it.
### Report sources become Project Evidence
- Linking a profile to a Project now adds supported web sources from its existing reports to
Evidence and continues adding sources from future reports.
- Evidence keeps its connection to the originating report and source, making it easier to review the
material behind an investigation.
- Sync status and retry controls make it clear when sources are still being added or need another
attempt.
See the [Projects overview](/docs/projects) and
[organizing intelligence in Projects](/docs/projects/organizing-intelligence).
---
# Platform Changelog (/docs/changelog)
Follow product changes across intelligence coverage, Chat, API access, and platform operations.
The first entry describes the current release. Monthly archives preserve the behavior announced
at that time; linked feature and API guides describe the platform as it works today.
## Current release: August 7, 2026
Y2 makes API results more complete and predictable, strengthens access checks around Automations,
and updates profile limits across Free, Lite, and Pro.
### More reliable API results
- Filtered list endpoints now continue through their underlying data until they can return a full
matching page or reach the end, preventing false-empty and incomplete pages.
- API errors use stable public codes and messages without exposing internal Convex details.
- Response validation and the API contract now agree across reports, projects, Automations,
profiles, webhooks, and Situation Room data.
- API Workbench responses preserve documented JSON, text, and audio behavior more consistently.
See the [API overview](/docs/api), [OpenAPI guide](/docs/api/openapi), and
[API Workbench](/docs/api/workbench).
### Safer Automations
- Automation runs revalidate workspace write access before reserving or spending credits. If access
changes during a run, Y2 stops the work and settles the reservation safely.
- API authorization failures now return the same status and error shape across Project and
Automation routes.
### Updated profile limits and Lite trial
- Pro supports up to **5 custom profiles**, up to **20 community or global subscriptions**, and a
**20-profile combined workspace total**.
- Lite continues to include **1 custom profile** and **3 community subscriptions**, does not include
reusable branding templates, and starts with a **7-day trial** during onboarding.
- Free supports **1 global or community profile total**. Elite limits and pricing are unchanged.
See [Plans and Pricing](/docs/plans) and [Creating Profiles](/docs/profiles/creating).
## Previous releases
Earlier release notes are grouped into monthly archives. Each link opens the full release entry.
### August 2026
- [August 7: Reliable API results, safer access, and updated plan limits](/docs/changelog/2026-08#august-7-2026-reliable-api-results-safer-access-and-updated-plan-limits)
- [August 5: Connected Explorer, Documents, and Project Evidence](/docs/changelog/2026-08#august-5-2026-connected-explorer-documents-and-project-evidence)
[View the complete August 2026 archive →](/docs/changelog/2026-08)
### July 2026
- [July 30: Privacy, product platform, and installed-app foundation](/docs/changelog/2026-07#july-30-2026-privacy-product-platform-and-installed-app-foundation)
- [July 24: Projects, Automations, and workspace governance](/docs/changelog/2026-07#july-24-2026-projects-automations-and-workspace-governance)
- [July 21: Global coverage, Chat updates, and Pro API keys](/docs/changelog/2026-07#july-21-2026-global-coverage-chat-updates-and-pro-api-keys)
- [July 17: Y2 Affiliate Program](/docs/changelog/2026-07#july-17-2026-y2-affiliate-program)
- [July 10: Privacy-first Chat and connected intelligence](/docs/changelog/2026-07#july-10-2026-privacy-first-chat-and-connected-intelligence)
- [July 8: Agent Y2 API, OpenAI-compatible Chat, and MCP](/docs/changelog/2026-07#july-8-2026-agent-y2-api-openai-compatible-chat-and-mcp)
[View the complete July 2026 archive →](/docs/changelog/2026-07)
### June 2026
- [June 30: News Terminal, Lite trial signup, and platform reliability](/docs/changelog/2026-06#june-30-2026-news-terminal-lite-trial-signup-and-platform-reliability)
- [June 18: Chat reliability, OSINT Monitor, and landing performance](/docs/changelog/2026-06#june-18-2026-chat-reliability-osint-monitor-and-landing-performance)
- [Y2 Elite B2B, Organization Workspaces and Intel API v2](/docs/changelog/2026-06#june-2026-y2-elite-b2b-organization-workspaces-and-intel-api-v2)
[View the complete June 2026 archive →](/docs/changelog/2026-06)
### March 2026
- [Y2SOURCE Event Provenance Engine](/docs/changelog/2026-03#march-2026-y2source-event-provenance-engine)
- [Profile Sharing Defaults](/docs/changelog/2026-03#profile-sharing-defaults)
- [Situation Room Map Enhancements](/docs/changelog/2026-03#situation-room-map-enhancements)
[View the complete March 2026 archive →](/docs/changelog/2026-03)
---
# Intelligence Chat (/docs/chat)
Y2 Intelligence Chat combines a selectable language model with platform tools for research,
documentation, news, profiles, and intelligence data. Chat is available in active Lite, Pro, and
Elite workspaces.
```mermaid
flowchart LR
A[Your message] --> B[ZDR-eligible model]
B --> C{Tool needed?}
C -->|No| D[Stream response]
C -->|Yes| E[Entitled Y2 tool]
E --> B
D --> F[Store thread and usage]
```
## Start a conversation
### Open Chat
Select **Chat** in the signed-in app. Free workspaces see an upgrade wall because their chat
credit limit is zero.
### Select a model
Open the model selector and choose an available model. The list is populated from Y2's current
ZDR-eligible catalog, so names and availability can change between visits.
### Describe the outcome
Ask a direct question or use a quick action for web search, InfoOps setup, topic research, or
Y2 documentation.
### Review tool activity
The thread displays tool activity and structured result cards when the model searches data or
proposes a profile change. Verify consequential results before confirming a write action.
Press Enter to send and Shift+Enter for a new line. While a response is streaming, use the stop
control to end generation. You can change the model for an existing thread or regenerate the last
assistant response.
## Model routing and privacy
The selector returns only model records marked as supporting Zero Data Retention. At request time,
Y2 also asks OpenRouter to deny provider data collection, require compatible request parameters,
and route only to ZDR endpoints.
ZDR applies to external model routing. Y2 still stores chat threads, messages, tool results,
workspace context, attachments when used, and usage records to operate the product. Tools such
as web search or image generation also send the necessary prompt or query to their configured
service.
If a saved model is no longer ZDR-eligible, Y2 falls back to an eligible default when one exists;
otherwise thread creation or model selection fails with a model-unavailable error.
## Available tools
Tools are attached to a request according to the active workspace's entitlements and the selected
model's capabilities.
Searches the local Y2 documentation corpus for product, API, profile, and delivery questions.
This tool is present in standard chat requests.
Searches current external sources when recent information is needed. It is enabled for Lite,
Pro, and Elite chat.
Searches cached News signals, filters by text or sentiment, retrieves available recaps, and
lists the current 40-feed catalog. Longer recap timeframe identifiers can be requested, but
the current News registry schedules only 12-hour recaps.
Searches shared/community profiles and can subscribe after confirmation. When the workspace
can create custom profiles, Chat can also collect profile fields, create a profile, inspect an
existing profile, preview changes, and update it. Normal profile and subscription limits still
apply.
Entitled paid workspaces can query Situation Room data. The current tool set includes OSINT
search, entities, incidents, cyber intelligence, FININT, markets, investigations, and
asynchronous investigation enrichment where the corresponding feature is enabled.
Paid workspaces can generate images. A dedicated image tool is attached when the selected
chat model does not provide native image generation; native-capable models handle the request
directly. Generated images use a separate feature-credit reservation and ledger.
A zero-cost internal tool can still be part of a metered chat response. Model input and output
tokens consume chat credits even when the tool itself adds no external-service charge.
## Write actions and confirmation
Some tools can change Y2 data:
- Follow a public or community profile.
- Create a custom InfoOps profile.
- Update profile settings or status.
- Start investigation enrichment.
The profile flows are designed to search the shared library first, preview proposed settings or
changes, and wait for explicit confirmation before creating or updating. Read the structured card
carefully, especially the profile, schedule, active workspace, and community-sharing setting.
## Chat credits
One credit represents $0.01 of normalized usage. Y2 meters model input and output using the
selected model's pricing data or a fallback pricing tier when provider pricing is absent. Certain
tools add usage, while internal cache reads and profile mutations currently add no separate tool
charge.
| Plan | Base chat allocation | Chat access |
| --- | ---: | --- |
| Free | 0 credits | No |
| Lite | 100 credits per month | Yes |
| Pro | 1,000 credits per month | Yes |
| Elite | 5,000 credits per month | Yes, with workspace governance views |
The usage meter reports credits used, limit, remaining credits, percentage, message count, input
tokens, output tokens, period, and last update time.
### Billing periods
Paid workspaces use the active subscription's billing boundaries. A monthly subscription receives
the base monthly allocation for that period. A yearly subscription receives 12 times the monthly
allocation for its yearly period. If no active paid-subscription period is available, usage falls
back to the calendar month.
At 80% usage the UI displays a warning, at 95% a critical warning, and at 100% it disables new
messages until the next billing period.
Some interface labels say “monthly” even for annual subscriptions. The backend limit and reset
boundary follow the actual subscription interval.
## Organize and remove conversations
Chat stores threads for the signed-in user and loads recent history in the sidebar. Threads can be
associated with a Y2 project, renamed, switched to another eligible model, and deleted.
Deleting a thread also deletes its stored chat messages. Treat deletion as permanent and export
or copy anything you need first.
## Troubleshoot Chat
Confirm the active workspace is Lite, Pro, or Elite. Entitlements follow the active workspace,
not just the signed-in user's other subscriptions.
The selector includes only models currently stored as ZDR-supported. Choose another available
model; a featured or previously saved model can disappear when eligibility changes.
Check the credit meter. At 100% of the current billing-period allocation, new messages are
blocked until the period resets.
Tool availability depends on the active workspace entitlement, profile limits, and selected
model capabilities. Switch workspaces or review plans when the requested feature is not
enabled.
Try another eligible model or a smaller request. Provider credit, rate, capability, or routing
failures can occur even when Y2 chat credits remain.
## Next steps
Stream the agent from an application with a scoped API key.
Review every field available in the profile editor.
Inspect the cache that powers Chat's news tool.
---
# Contribute to Y2 (/docs/community/contributing)
Choose the contribution path that matches what you want to improve. Product and documentation
feedback goes to Y2; public briefing profiles are published inside the application; code changes
belong in the repository that owns the relevant SDK or tool.
## Choose a contribution path
| Your goal | Use this path | What to include |
| --- | --- | --- |
| Publish a reusable briefing | Enable **Share with Community** on a workspace-owned profile | Clear topic, safe public instructions, useful tags, and reviewed output |
| Report a product or documentation problem | Use PostHog Support in the desktop app or email [support@y2.dev](mailto:support@y2.dev) | Route or docs URL, expected result, actual result, and reproduction details |
| Report unsafe or inaccurate public content | Email [support@y2.dev](mailto:support@y2.dev) | Profile name or URL, the specific concern, and supporting context |
| Build an integration | Use the OpenAPI contract, API keys, and webhooks | Least-privilege scopes, signature verification, retries, and idempotency |
| Change an SDK or CLI | Follow the contribution and license terms in that tool's own repository | Focused change, matching tests, documentation, and compatibility notes |
```mermaid
flowchart TD
idea["Contribution"] --> content{"What changes?"}
content -->|Public briefing| share["Publish from profile settings"]
content -->|Product or docs| feedback["PostHog Support or support email"]
content -->|Security issue| private["Private support email"]
content -->|SDK or CLI code| repository["Tool-specific repository process"]
content -->|Integration| contract["OpenAPI + webhooks"]
```
## Publish a community briefing
Community sharing is the only in-product publication path documented here. It makes the original
profile and its later reports discoverable to other signed-in Y2 users; it does not submit the
profile to a review queue.
### Prepare a focused profile
Use a clear name, topic, report structure, and compact set of tags. Generate and review at least
one report before asking others to rely on it.
### Remove private context
Review the custom instructions, sources, sections, and generated output. Remove credentials,
customer data, internal names, and confidential research requirements.
### Enable sharing
Open Advanced Configuration, enable **Share with Community**, and save the profile.
### Verify the public result
Open [InfoOps → Discover](/app/infoops/community), search for the profile, and inspect its
details as a reader would.
See [Share a Profile](/docs/profiles/sharing) for the complete visibility and subscriber model.
## Report a product or documentation problem
The desktop application loads PostHog Support when analytics consent is enabled. The widget is
hidden on mobile and while Copilot is open. Email [support@y2.dev](mailto:support@y2.dev) when the
widget is unavailable or the report needs a private channel.
A useful report contains:
- The application route or documentation URL.
- The active workspace type and plan when the behavior is entitlement-related.
- A short, repeatable sequence of actions.
- The expected and actual result.
- Relevant timestamps, request IDs, HTTP status codes, or screenshots.
- Whether the issue is consistent or intermittent.
For a documentation correction, quote only the smallest inaccurate statement and point to the
current UI, API response, or other evidence that contradicts it.
Never include API keys, webhook signing secrets, one-time authentication codes, payment details,
or unredacted personal data. Redact authorization headers and sensitive payload fields before
attaching logs or screenshots.
## Report a security concern privately
Send a security concern to [support@y2.dev](mailto:support@y2.dev). Include the affected surface,
impact, reproduction conditions, and a safe proof of concept. Do not publish exploit details,
credentials, or another workspace's data in a community profile or public issue.
Y2's local product repository does not define a separate public security-policy file, so this page
does not promise a public issue tracker, bounty, response time, or disclosure schedule.
## Build an integration
Use the documented API contract rather than depending on application internals.
Generate clients from the same specification used by the endpoint reference
Create tenant-scoped keys and grant only the required scopes
Verify signatures and design receivers for retries and duplicate delivery
Start from task-oriented API patterns
There is no documented public integration marketplace or automatic submission process in the
current application. Contact support before presenting an integration as endorsed by Y2.
## Work on an SDK or CLI
The SDK pages link to their current source and package locations. Before changing a tool, open its
own repository and verify its README, contribution instructions, tests, release process, and
license. Those repository-local rules take precedence over this general guide.
Review the TypeScript package, examples, and linked source repository
Review the Python package, examples, and linked source repository
Review CLI installation, commands, and linked source repository
The Y2 platform repository identifies itself as proprietary, and the API contract uses
proprietary terms. Do not assume that the platform, documentation, examples, and external SDK
repositories share one license. Check the license attached to the exact artifact you plan to use
or modify.
## Contribution checklist
- Keep the change focused on one user problem.
- Verify examples against the current API contract or application behavior.
- Include tests when the target repository has a test suite.
- Update the documentation affected by a behavior or interface change.
- Avoid promising badges, editorial review, promotion, credits, or response times unless Y2 has
confirmed them for the specific contribution.
- Use [@y2_intel on X](https://x.com/y2_intel) for public updates; use support channels for
actionable reports.
---
# Community (/docs/community)
Y2 Community is the public briefing catalog inside **InfoOps → Discover**. It combines profiles
curated by Agent Y2 with profiles that users have marked **Share with Community**.
This catalog is about reusable InfoOps profiles and their reports. It is separate from the
“emergent signals” extracted inside reports and intelligence APIs.
## How community profiles work
```mermaid
flowchart LR
author["Profile owner"]
toggle["Share with Community"]
catalog["Discover catalog
Global + community profiles"]
follow["Follow in active workspace"]
reports["My Briefings
Reports + delivery settings"]
author --> toggle --> catalog --> follow --> reports
```
The profile remains owned by its creator. Following it creates an active subscription in your
current workspace; it does not copy the profile or let you edit the creator's research settings.
## What you can do
Browse global, city, industry, and user-published profiles; filter the catalog; and follow a
briefing
Make a profile and its generated reports discoverable to other Y2 users
Review subscriptions and configure delivery from My Briefings
Report a problem, improve documentation, or build against the documented API
## Catalog organization
Discover groups public profiles using their stored flags and tags:
| Browse group | Current rule |
| --- | --- |
| Featured | Agent Y2 global profile without a `city` or `industry` tag |
| Cities | Any public profile tagged `city` |
| Industries | Any public profile tagged `industry` |
| Public | User-shared profile that is not classified as city or industry |
These are browse rules, not editorial certifications. Tags influence where a profile appears.
The catalog also supports search across name, topic, custom prompt, and tags; filters for public
type, subscription status, profile status, tags, audio, and saved recursion depth; and sorting by
recent delivery, name, creation date, or subscriber value.
## Subscription limits
Subscriptions are counted in the active workspace.
| Plan | Public-profile capacity |
| --- | --- |
| Free | 1 global or community subscription |
| Lite | 3 global or community subscriptions |
| Pro | Up to 20 total profiles, including custom profiles and subscriptions |
| Elite | Up to 50 total profiles, including custom profiles and subscriptions |
The subscription counts even if delivery is paused. Unfollow a profile from Discover or
My Briefings to remove its subscription record and free capacity.
## Public-data boundary
When a creator enables **Share with Community**, the profile becomes discoverable immediately; the
current application path does not create a moderation or approval queue. Other signed-in users can
inspect the profile topic, creator label, schedule and research configuration, subscriber and report
counts, and available generated reports.
Review the profile's topic, custom prompt, tags, configuration, and generated reports before
sharing. Community visibility is a publication control, not private team sharing.
## Quality and reporting
Y2 displays activity, report count, subscriber count, creator, tags, and configuration details, but
the current code does not assign a “Verified Analyst” badge or guarantee content review. Evaluate a
profile's topic and latest report before following it.
To report inaccurate, inappropriate, or unsafe public content, email
[support@y2.dev](mailto:support@y2.dev) with the profile name or URL and a concise description.
## Related guides
Browse, filter, inspect, follow, and unfollow public briefing profiles
Understand exactly what becomes public and how to turn sharing off
Compare profile capacity and delivery features
---
# Browse Public Briefings (/docs/community/signals)
Use **InfoOps → Discover** to find public briefing profiles and follow them in your active
workspace. The catalog includes Agent Y2 global profiles and profiles that users have enabled for
community sharing.
This page covers the briefing catalog. It is separate from the signal objects returned by the
intelligence APIs.
## Before you follow a profile
Check the workspace switcher first. A follow belongs to the active workspace and consumes that
workspace's profile capacity; switching workspaces later does not move the subscription.
| Plan | Capacity used by public briefings |
| --- | --- |
| Free | Up to 1 global or community subscription |
| Lite | Up to 3 global or community subscriptions |
| Pro | Subscription counts toward the 20-profile total |
| Elite | Subscription counts toward the 50-profile total |
Pausing delivery does not free capacity. Unfollow the profile to remove its subscription record.
## Find and follow a briefing
### Open Discover
In the active workspace, open [InfoOps → Discover](/app/infoops/community).
### Choose a browse group
Use **All**, **Featured**, **Cities**, **Industries**, or **Public**. With no search or tag
filters, the All view shows grouped previews and up to 12 profiles per group. Choose **View
All** when a group contains more results.
### Narrow the catalog
Search by profile name, topic, custom instructions, or tags. Then combine the type, status,
audio, saved depth, tag, and sort controls as needed.
### Inspect the profile
Open the details view before following. Review the creator, topic, instructions, cadence,
source and model configuration, activity status, latest report, and audio setting. Report and
subscriber counts appear when available.
### Follow in the active workspace
Select **Follow**. Y2 creates a workspace-scoped subscription to the original profile; it does
not clone the profile or grant permission to edit it.
```mermaid
flowchart LR
catalog["Public catalog"] --> group["Browse group"]
group --> search["Search + filters"]
search --> details["Inspect profile details"]
details --> follow["Follow in active workspace"]
follow --> briefings["My Briefings"]
```
## How profiles are grouped
Discover derives its groups from stored profile flags and tags. The labels are browse rules, not
editorial certifications.
| Group | Current catalog rule |
| --- | --- |
| Featured | Agent Y2 global profile without a `city` or `industry` tag |
| Cities | Public profile tagged `city` |
| Industries | Public profile tagged `industry` |
| Public | User-shared profile not classified as city or industry |
A `city` or `industry` tag takes precedence over the Featured grouping, even when the profile is
also global.
## Search, filter, and sort
| Control | Available behavior |
| --- | --- |
| Search | Matches name, topic, custom instructions, and tags; spaces, slashes, and hyphens are normalized for compact matching |
| Type | All, Featured/global, or Public/user-shared |
| Subscription | All, Following, or not currently followed through the status controls |
| Profile status | All, Active, or Inactive |
| Audio | Any, has audio enabled, or has no audio |
| Saved depth | Quick for no saved depth or `0`; Standard for `1–2`; Deep for `3+` |
| Tags | Matches a profile when it has at least one selected tag |
| Sort | Alphabetical, most recent delivery, most followed, or recently created |
The depth badge describes the profile's saved configuration. Report execution currently caps
recursive research at depth 1; see [Research Workflows](/docs/workflows) for the runtime behavior.
Subscriber counts are available in profile details, but the current catalog-list payload does
not guarantee a count for every card. Profiles without a value can tie in **Most Followed**
ordering, so inspect the profile and its latest report instead of treating the sort as a quality
ranking.
## What following changes
Following a profile:
- Adds one subscription record to the active workspace.
- Makes the profile available from **My Briefings**.
- Preserves the creator's ownership and research configuration.
- Lets the subscriber use delivery controls supported by their plan.
- Receives later reports from the same shared profile while the subscription remains active.
The creator can change the profile or stop sharing it. Existing subscription records are not
automatically cloned or converted into workspace-owned profiles.
To free capacity, choose **Following** in Discover or open **My Briefings**, then unfollow the
profile. Turning a subscription inactive only pauses it and still counts toward the limit.
## App and API boundary
Use the application to browse the public catalog and create or remove community subscriptions.
The public [`GET /profiles`](/docs/api/reference/profiles/listProfiles) endpoint lists profile
subscription records accessible to the authenticated workspace; it is not a public-catalog search
endpoint.
Use the generated [Profiles API reference](/docs/api/reference/profiles) for the current request,
response, authentication, and scope contract.
## Troubleshooting
Confirm the active workspace and its plan. Free and Lite have dedicated public-subscription
limits; Pro and Elite combine owned profiles and followed profiles in one total. Unfollow an
unused profile to free capacity.
Clear the search, selected tags, and type or status filters. Then return to **All**. Also verify
that you did not switch workspaces when checking which profiles are already followed.
Inspect its details and latest report before following. The catalog can display inactive public
profiles; visibility does not guarantee a future publishing cadence.
Following references the creator's profile. Create a new workspace-owned profile when you need
different instructions, sources, cadence, or sections.
## Related guides
Review catalog visibility, quality signals, and subscription limits
Publish a workspace-owned profile and understand what becomes visible
Create and maintain workspace-owned and followed briefings
Check profile, delivery, API, and workspace limits
---
# Email Delivery (/docs/delivery/email)
Email is the default delivery method for an Information Operations subscription. Y2 renders the
generated report as an HTML email and sends it to the subscription owner's current account email.
```mermaid
flowchart LR
A[Scheduled profile] --> B[Report generated]
B --> C{Delivery preference}
C -->|Email| D[Account email]
C -->|Email + SMS| D
C -->|Elite workspace audience| E[Eligible workspace members]
```
## Configure email for a profile
Delivery preferences belong to a profile subscription. Changing one profile does not change the
others.
### Open My Profiles
Go to **InfoOps → My Profiles**.
### Open Delivery Preferences
Open the profile card's actions menu, then select **Delivery Preferences**. You can also find
the same controls under **Advanced** when editing an existing profile.
### Select Email Only
Choose **Email Only**. If a stored subscription has no delivery method, Y2 also treats it as
email.
### Choose the audience
Keep **Individual** to send to your account email. Eligible Elite workspaces can select
**Workspace** to include workspace members.
### Save preferences
Select **Save Preferences**. Saving also makes the subscription active.
A new profile must be created before its subscription-level delivery preferences are available.
Until then, the default method is email to the individual account.
## Verify or change the recipient email
Email delivery uses the email stored on your Y2 account at the time the report is sent.
1. Open **Settings** in the app.
2. Find the **Email** account section.
3. Confirm the current verified address.
4. To change it, enter the new address and complete the emailed one-time-code flow.
After verification, subsequent individual deliveries use the updated address. Y2 does not expose
an arbitrary recipient-address field on each profile.
## Individual and workspace audiences
| Audience | Availability | Recipients |
| --- | --- | --- |
| Individual | All plans | Subscription owner's current account email |
| Workspace | Elite workspaces with member support | Owner plus eligible non-suspended members with account emails |
The workspace audience is bounded by the workspace's member entitlement. A missing email on a
member record prevents delivery to that member.
Workspace delivery can expose the complete report to other members. Confirm the profile topic,
report content, and workspace membership before enabling it.
## Email contents
The current report template can include:
Profile name, topic, generated report body, and available intelligence sections.
Sanitized citations from the report or a source-link section when separate citations are not
already present.
A link to the in-app report page when audio narration exists. Audio is not attached.
Assigned logo, colors, terminology, and before/after static content when configured.
The email subject is the profile name. The preview text uses the configured branding prefix plus
that name. Report and branding HTML are sanitized before rendering.
See [Branding Templates](/docs/profiles/branding) for the fields that can change email appearance
and copy.
## Combine delivery channels
The stored delivery method is one of four values:
There is currently no combined Email + Webhook option. Select **Webhook** only when the endpoint
should replace the email channel for that subscription.
## Delivery timing and history
Y2 attempts delivery after report generation reaches the delivery stage. Generation duration,
queueing, retries, and the email provider affect arrival time; there is no fixed five-minute
delivery guarantee.
Open **InfoOps → History** to inspect the recorded date, profile, status, method, and delivery
details. A successful send request means Y2 queued or sent the message through its email provider;
the recipient's provider can still filter, defer, bounce, or reject it.
## Troubleshoot missing email
Confirm that the profile and subscription are active, then review its next scheduled run. A
delivery cannot occur until report generation succeeds.
Open **Delivery Preferences** and confirm that the method is **Email Only** or **Email + SMS**.
**SMS Only** and **Webhook** do not send the report email.
Open **Settings → Email**, complete verification if needed, and confirm the address before the
next delivery.
Review the delivery details, correct the account or workspace-member email, and wait for the
next run. For persistent failures, contact [support@y2.dev](mailto:support@y2.dev).
Check spam, quarantine, inbox rules, and the receiving provider's logs. Allow the exact sender
shown in a received Y2 message; the configured sender address can vary by environment.
## Next steps
Add a verified-phone summary channel on Pro or Elite.
Replace email delivery with an authenticated endpoint.
Understand when profile generation begins.
---
# SMS Delivery (/docs/delivery/sms)
SMS delivery sends an intelligence-report summary to the subscription owner's verified phone.
It is available to active Pro and Elite workspaces.
SMS requires **Pro or Elite**, a verified account phone, and a profile delivery preference set
to **SMS Only** or **Email + SMS**.
```mermaid
flowchart LR
A[Verified account phone] --> B[Profile subscription]
B --> C[Report generated]
C --> D{SMS summary exists?}
D -->|Yes| E[Use generated summary]
D -->|No| F[Extract bounded report summary]
E --> G[SMS provider]
F --> G
```
## Add and verify a phone
### Open account settings
In the app, go to **Settings → Account** and find **Phone**.
### Enter the number
Select a country code, enter the local number, and request a verification code. The current
input supports common country-code presets and stores the combined international number.
### Verify ownership
Enter the six-digit code sent to that phone. SMS delivery remains unavailable until the
account has a phone verification timestamp.
Use a number that can receive SMS. Standard carrier message and data rates may apply.
## Enable SMS for a profile
### Open Delivery Preferences
Go to **InfoOps → My Profiles**, open the profile card's actions menu, and select **Delivery
Preferences**.
### Choose a method
Select **SMS Only** to omit email, or **Email + SMS** to receive both channels.
### Save
Select **Save Preferences**. The backend rechecks the active workspace entitlement and phone
verification before accepting an SMS method.
| Method | Email | SMS |
| --- | --- | --- |
| SMS Only | No | Yes |
| Email + SMS | Yes | Yes |
SMS is sent only to the subscription owner's account phone. The Elite **Workspace** audience
setting applies to email recipients, not to member phone numbers.
## Message contents
Y2 first uses the report's pre-generated `smsSummary` when present. If a report does not have one,
the delivery path extracts a concise section from the HTML report and prefixes it with the profile
name.
```text
Y2 Cyber Risk:
Critical infrastructure advisories increased across the monitored sources...
```
The fallback formatter targets a 140-character message and truncates its extracted summary when
needed. Generated `smsSummary` content is also instructed to be concise, but carrier segmentation
can vary with message length and character encoding.
The current SMS template does not guarantee a link to the full report. Open Y2 directly when
you need the report body, citations, or audio.
## Timing, failures, and limits
SMS is attempted after report generation enters the delivery stage. When **Email + SMS** is
selected, the current workflow attempts email before SMS. Queueing and provider response times
vary, so there is no fixed one-minute arrival guarantee.
The send provider can reject a request for reasons including rate limits, an invalid number or
message, authentication, or service availability. Y2 records SMS success or failure in
**InfoOps → History** when the provider call reaches the SMS delivery action.
Y2 does not currently document a fixed per-account daily SMS quota. Plan access and provider
limits still apply.
## Change or remove the phone
Open **Settings → Account → Phone** to change or remove the verified number.
- Changing the number requires a new one-time-code verification.
- Removing it clears the phone and verification timestamp from the account.
- Removing a phone prevents later SMS delivery, but it does not rewrite every stored profile
preference. Change affected profiles to **Email Only** if you still want delivery.
The SMS provider necessarily receives the destination number and message content to deliver the
text. Do not place secrets or highly sensitive material in profile instructions that can appear in
a report summary.
## Troubleshoot SMS delivery
Confirm that the active workspace is Pro or Elite and that **Settings → Account → Phone**
shows a verified number.
Confirm the selected country code and local number, then retry. Verification requests can fail
when the SMS provider is unavailable or the number is already associated with another account.
Verify that the profile uses **Email + SMS**, not **Email Only**. Then review **InfoOps →
History** for an SMS-specific failure.
Add and verify a phone again, then reopen Delivery Preferences. Stored SMS selections cannot
deliver without a current verified account phone.
Check the History details and retry on the next report run. For persistent authentication or
service failures, contact [support@y2.dev](mailto:support@y2.dev).
## Next steps
Receive the complete HTML report and source links.
Deliver structured events to an endpoint instead of email or SMS.
Compare delivery features across workspace plans.
---
# Webhook Delivery (/docs/delivery/webhooks)
Webhook delivery sends a signed CloudEvents 1.0 notification to an HTTPS endpoint after a report
is published. It is available to active Lite, Pro, and Elite workspaces.
```mermaid
flowchart LR
A[Report published] --> B[CloudEvent persisted]
B --> C[Signed POST]
C --> D{Endpoint returns 2xx?}
D -->|Yes| E[Success recorded]
D -->|No| F[Failure count incremented]
F --> G[Disabled after 5 consecutive failures]
```
A webhook notification is compact. It identifies the report and provides API links; it does
not embed the full HTML report or all source records.
## Create an endpoint in Y2
You do not need an API key when configuring webhooks in the app.
### Open Webhooks
In an active paid workspace, go to **Developers → Webhooks**.
### Create a configuration
Enter a name and public endpoint URL. Production webhook URLs must use HTTPS. Localhost,
loopback, private-network, link-local, and other blocked addresses are rejected.
### Add verification and authentication
Generate or enter an optional signing secret. You can also add custom string headers, such as
an authorization token for your endpoint.
`Host`, `Content-Length`, and `Content-Type` cannot be supplied as custom headers. Y2's own
delivery headers take precedence over custom values.
### Save and test
Create the webhook, then select **Test Webhook**. The test sends a
`dev.y2.webhook.test.v1` CloudEvent and waits up to five seconds for a response.
### Assign it to a profile
Go to **InfoOps → My Profiles → Delivery Preferences**, choose **Webhook**, select the active
configuration, and save.
Selecting **Webhook** replaces email and SMS for that subscription. There is no combined
Email + Webhook delivery method.
## Receive a report event
Report deliveries use `Content-Type: application/cloudevents+json` and the event type
`dev.y2.report.generated.v1`.
```json
{
"specversion": "1.0",
"id": "y2mexampleeventid",
"source": "https://api.y2.dev/api/v1/profiles/prf_0123456789abcdef01234567",
"type": "dev.y2.report.generated.v1",
"subject": "reports/rpt_0123456789abcdef01234567",
"time": "2026-07-21T12:00:00.000Z",
"datacontenttype": "application/json",
"dataschema": "https://api.y2.dev/schemas/events/report-generated-v1.json",
"data": {
"report": {
"id": "rpt_0123456789abcdef01234567",
"profileId": "prf_0123456789abcdef01234567",
"summary": "Concise report summary...",
"status": "published",
"generatedAt": "2026-07-21T11:59:48.000Z",
"language": "en",
"intelligence": {
"signalCount": 4,
"graphNodeCount": 12
},
"audio": {
"status": "available",
"durationSeconds": 318
}
},
"subscription": {
"id": "sub_0123456789abcdef01234567"
},
"links": {
"report": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567",
"markdown": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?format=markdown",
"sources": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567?include=sources",
"signals": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/signals",
"graph": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/graph",
"audio": "https://api.y2.dev/api/v1/reports/rpt_0123456789abcdef01234567/audio"
}
}
}
```
`summary` can be `null`, and audio can be marked `unavailable`. Following a link can require an
appropriately scoped API key or the endpoint's documented x402 flow.
## Request headers
"',
},
}}
/>
## Verify the signature
Compute HMAC-SHA256 over the exact raw request body bytes using the configured secret. Prefix the
hex digest with `sha256=` and compare it with `X-Y2-Signature` in constant time.
```javascript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyY2Webhook(rawBody, headers, secret) {
const supplied = headers["x-y2-signature"] ?? "";
const timestamp = Number(headers["x-y2-timestamp"] ?? 0);
const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - timestamp);
if (!timestamp || ageSeconds > 300) return false;
const expected = `sha256=${createHmac("sha256", secret)
.update(rawBody)
.digest("hex")}`;
const suppliedBytes = Buffer.from(supplied);
const expectedBytes = Buffer.from(expected);
return (
suppliedBytes.length === expectedBytes.length &&
timingSafeEqual(suppliedBytes, expectedBytes)
);
}
```
Use the raw bytes before JSON parsing; re-serializing an object can change the signed body. Keep
your own replay window policy and deduplicate with `Idempotency-Key` or `X-Y2-Event-Id`.
A secret is optional in the configuration form. If you omit it, Y2 does not send
`X-Y2-Signature`; secure the endpoint with a custom authorization header or another control.
## Acknowledge deliveries safely
The production delivery request has a 10-second timeout. Return a `2xx` response only after the
event is durably accepted, then process long-running work asynchronously.
```javascript
app.post("/y2/webhook", rawBodyMiddleware, async (request, response) => {
if (!verifyY2Webhook(request.rawBody, request.headers, process.env.Y2_WEBHOOK_SECRET)) {
return response.sendStatus(401);
}
await queue.put({
id: request.headers["x-y2-event-id"],
event: JSON.parse(request.rawBody.toString("utf8")),
});
return response.sendStatus(202);
});
```
Y2 does not run an independent automatic webhook retry loop. A timeout, network error, or non-2xx
response increments the configuration's consecutive failure count. A successful delivery resets
the count; five consecutive failures disable the webhook.
## Manage configurations
- Editing a masked webhook leaves its existing secret unchanged when the secret field is blank.
- Activating a webhook resets its failure count.
- An inactive webhook cannot be assigned for delivery.
- A configuration cannot be deleted while any subscription references it. Reassign those profiles
first.
- The app exposes 30-day success/failure statistics, last-use time, subscription count, and current
consecutive failures.
You can also manage configurations through the public API. API-key requests use the
`webhooks:manage` scope; creating new API keys requires Pro or Elite, while webhook delivery itself
is available on Lite.
## Next steps
Review management endpoints and integration boundaries.
Inspect canonical request and response schemas.
Retrieve report content linked by an event.
---
# Getting Started (/docs)
Set up Y2, choose what you want to monitor, and confirm where your intelligence reports will
arrive.
In about 10 minutes, you will have an active workspace, at least one intelligence profile, and
a delivery path you can verify in **My Profiles**.
## How Y2 works
```mermaid
flowchart LR
topic["Choose a topic"] --> profile["Create or subscribe to a profile"]
profile --> research["Y2 researches and synthesizes"]
research --> report["Receive or open the report"]
```
An intelligence profile defines the subject Y2 monitors. Y2 runs research for that profile and
makes each completed report available in the app and through the delivery methods included with
your workspace plan.
## Before you begin
You need an email address or phone number that can receive a verification code. No API key is
required for this tutorial.
A Free workspace can subscribe to one community profile. Custom-profile creation and delivery
start with Lite. Pro adds scheduled-report management, SMS, audio, and new API keys. See
[Plans and limits](/docs/plans) for the complete reference.
## Set up your first intelligence workflow
### Sign in and finish onboarding
Open [Y2 sign in](/login), choose email or phone verification, and enter the code you receive.
Complete the workspace prompts before continuing.
Your setup is ready when Y2 opens your workspace dashboard.
### Choose how to start
Choose an existing briefing when you want a ready-made topic. This is the fastest path
and is available on Free.
Define your own topic, keywords, cadence, and delivery settings. Free includes one trial
profile; ongoing custom-profile delivery starts with Lite.
### Configure the profile
For a community profile, review its topic and cadence, then confirm the subscription.
For a custom profile, enter a clear name and topic, add useful keywords, and choose a
frequency. Start with a narrow subject you can recognize in the resulting report. The
[profile creation guide](/docs/profiles/creating) explains every field.
### Choose an available delivery method
Email is the default. Webhook delivery requires Lite or higher; SMS requires Pro or Elite.
You can change delivery settings later from the profile.
Receive the formatted report at the verified email address.
Send report JSON to an HTTPS endpoint on Lite, Pro, or Elite.
Receive a concise mobile summary on Pro or Elite.
### Confirm the setup
Open [My Profiles](/app/infoops/my-profiles). Your new profile or subscription should appear
in the list with its current status and delivery method.
The setup is complete when you can open the profile from this page. Custom reports follow the
configured cadence; completed reports remain available from the profile.
## Choose your next path
Learn how topics, schedules, sharing, branding, and report delivery fit together.
Use the Situation Room, source health, country cards, and investigation surfaces.
Authenticate, test requests in the Workbench, and use the generated endpoint reference.
Give an agent the contract, documentation bundle, and repository-specific integration prompt.
## Understand the platform
- [Mission](/docs/mission) explains why Y2 makes intelligence workflows broadly accessible.
- [Vision](/docs/vision) describes the direction of the platform.
- [Plans and limits](/docs/plans) lists current workspace capabilities and quotas.
- For help with your account, email [support@y2.dev](mailto:support@y2.dev).
---
# LLM-friendly Documentation (/docs/llms)
Y2 publishes build-generated text and MDX artifacts so an AI integration can discover, retrieve,
and cite the same documentation shown in Fumadocs.
Start with `/llms.txt`. Fetch one page when the task is narrow, use `/llms-full.txt` only when
broad product context is necessary, and use `/api/openapi.yaml` for request and response schemas.
## How the artifacts are produced
```mermaid
flowchart LR
source["content/docs MDX"] --> index["/llms.txt"]
source --> full["/llms-full.txt"]
source --> page["/docs/{slug}.mdx"]
source --> search["Fumadocs search index"]
openapi["public/api/openapi.yaml"] --> reference["Generated endpoint reference"]
```
The Vite documentation plugin reads `content/docs` during a production build. It emits the index,
full corpus, page-level MDX files, and the Fumadocs search index. During local development, the
same plugin generates these responses on request. The OpenAPI reference is generated separately
from `public/api/openapi.yaml`.
## Artifact reference
| Artifact | Content | Use it for |
|---|---|---|
| `/llms.txt` | Plain-text overview and grouped links to every documentation page | Discovery and routing |
| `/llms-full.txt` | Concatenated page titles, canonical paths, and source MDX bodies | Broad grounding or corpus ingestion |
| `/docs.mdx` | Source MDX body for the documentation home page | Targeted retrieval |
| `/docs/{slug}.mdx` | Source MDX body for one documentation page | Task-specific context |
| `/api/openapi.yaml` | OpenAPI contract for public HTTP endpoints | Types, parameters, schemas, scopes, and responses |
These are public documentation artifacts and do not require a Y2 API key.
Page-level files and `/llms-full.txt` contain the MDX body without frontmatter. Fumadocs tags such
as `Callout`, `Cards`, and `APIPage` can remain in the text. Generated endpoint pages may point to
`public/api/openapi.yaml` instead of expanding every schema, so API clients must also read the
published OpenAPI contract.
## Retrieve documentation
```bash
curl -fsS https://y2.dev/llms.txt
```
Use the grouped links and descriptions to decide which page the task needs.
```bash
curl -fsS https://y2.dev/docs/api/authentication.mdx
```
Append `.mdx` to a canonical documentation path. For example, `/docs/api` becomes
`/docs/api.mdx` and `/docs/profiles/creating` becomes `/docs/profiles/creating.mdx`.
```bash
curl -fsS https://y2.dev/llms-full.txt
```
Use this response when the agent needs relationships across several documentation sections.
```bash
curl -fsS https://y2.dev/api/openapi.yaml
```
Treat the operation definitions and schemas here as authoritative for HTTP integration code.
The deployed files use revalidation caching. Re-fetch them when starting a new integration task or
after a Y2 release instead of treating a local copy as permanent.
## Ingest the full corpus safely
Each page in `/llms-full.txt` begins with a generated header:
```text
# Page Title (/docs/canonical-path)
[Source MDX body]
```
The plugin places `---` between pages, but source pages can also contain their own horizontal
rules. Do not split on every `---`. Detect generated page headers that match a title followed by a
`/docs` path, and retain that path as source metadata.
### Fetch and validate the response
Require a successful HTTP response and a text content type before replacing an existing corpus.
### Detect page boundaries
Start a new document at headers shaped like `# Title (/docs/path)`. Keep the title and path
separate from the MDX body.
### Chunk within each page
Split long pages by their Markdown headings while preserving the canonical page path on every
chunk. Keep code blocks intact.
### Index the API contract separately
Parse `/api/openapi.yaml` by operation and schema. Do not infer endpoint shapes from examples
or from an `APIPage` component tag in the MDX corpus.
## Use MCP when the agent needs tools
The LLM artifacts provide documentation context only. Use the Y2 MCP integration when an agent
also needs bounded report or News reads, OpenAPI operation lookup, or Agent Y2 access.
`@y2-intel/mcp` is a separate package whose implementation is not in this platform repository.
After installation, use the MCP client's `tools/list`, `resources/list`, and `prompts/list`
responses as the source of truth for that installed version.
The [MCP guide](/docs/api/mcp) covers client configuration, scoped credentials, capability
inspection, and troubleshooting. Keep `Y2_API_KEY` in the MCP process environment, never in a
prompt or tool argument.
## Prepare a coding agent
Use the maintained integration prompt when an agent will write Y2 API code. It directs the agent
to the documentation index, OpenAPI contract, current base URLs, authentication rules, and plan
boundaries.
Ground an agent, constrain its scope, and verify the generated integration.
Choose the correct server, validate the contract, and regenerate client types.
Configure and inspect the separately distributed MCP server.
Build server-side TypeScript, Python, or command-line integrations from the contract.
---
# Mission (/docs/mission)
Y2 exists to make useful intelligence accessible to people and organizations that do not have a
dedicated research team.
## The problem
Important signals are scattered across feeds, reports, markets, maps, and specialized data
providers. Following them manually creates three recurring problems:
- **Coverage gaps:** no single source contains the full picture.
- **Attention limits:** people cannot continuously monitor every relevant source.
- **Lost context:** one-off searches rarely preserve what changed or why it matters.
Traditional intelligence services can address these problems, but their cost and operating model
often put them out of reach for individuals and smaller teams.
## Y2's approach
Profiles turn a recurring question into an intelligence workflow with a defined topic,
research cadence, and delivery path.
Live feeds, reports, entities, incidents, and relationships remain connected so readers can
move from an alert to the supporting context.
Intelligence can remain in the workspace or move through email, SMS, webhooks, audio, and the
API when the active plan supports them.
Global and community profiles let one well-designed monitoring workflow benefit more than one
person.
## Operating principles
### User-defined priorities
The operator decides what to monitor, which sources and workflows are relevant, and where results
should go. Automation serves that intent rather than replacing it.
### Traceable evidence
Reports and intelligence objects should retain enough source and provenance context for readers to
inspect the underlying evidence and form their own judgment.
### Human judgment remains final
Y2 can collect, organize, and synthesize information. It does not remove uncertainty or make a
decision on the operator's behalf.
### Broad access
The platform should support an individual testing one briefing, an analyst building a repeatable
workflow, and a team operating shared intelligence resources.
Open-source data can be incomplete, delayed, duplicated, or wrong. Treat generated analysis as
decision support, review cited evidence, and apply appropriate professional judgment.
## Who Y2 serves
Y2 is designed for analysts, investors, security professionals, researchers, operators, and
business leaders who need repeatable awareness of complex or fast-changing topics.
## Put the mission into practice
Set up a workspace and verify your first intelligence profile.
Explore live events, sources, country context, and connected intelligence.
Bring Y2 intelligence into an application or operational workflow.
---
# News Signal Feed (/docs/news)
The News Signal Feed lets signed-in users monitor cached RSS and X signals across 40 topics.
Select the topics relevant to your work, then switch between individual signals and AI-generated
12-hour recaps.
```mermaid
flowchart LR
A[Configured RSS and X sources] --> B[Scheduled ingestion]
B --> C[Topic caches]
C --> D[Live Feed]
C --> E[12h Recap]
D --> F[Source link]
```
“Live Feed” describes the newest signals available in Y2's cache. RSS sources and recaps are
refreshed on scheduled cycles; X source ingestion currently runs on a separate weekly cycle.
## Monitor selected topics
### Open News
Sign in to Y2 and select **News** in the app navigation.
### Select feeds
Choose one or more chips under **Global Signal Feeds**. The current catalog contains 40
feeds organized into six groups:
| Group | Examples |
| --- | --- |
| Politics & Geopolitics | Geopolitics, US Politics, Defense |
| Macro & Markets | Macro, Equities, Rates & FX |
| Industry Verticals | Energy, Cyber, Biotech & Health |
| Regional Desks | MENA, Europe, Asia-Pacific |
| AI & Technology | AI, AI Agents, Machine Learning |
| Crypto & Digital Assets | Bitcoin, Ethereum, DeFi |
See [Topics and feeds](/docs/news/topics) for the complete catalog.
### Choose a view
Select **Live Feed** for individual signals or **12h Recap** for a per-topic synthesis.
### Open a source
When a signal has a source URL, select the row or **View Source** to open the original item
in a new tab.
Your selected feeds and view mode are saved to your account. Feed changes are written after a
short debounce, so the same selection can be restored on another signed-in device.
## Read a signal
Each Live Feed row can show:
Sentiment is a directional signal, not a confidence score or investment recommendation. Items
without a supported classification appear without a sentiment badge.
Treat generated sentiment and recaps as triage aids. Open the attributed source and verify
material claims before acting on them.
## Use 12-hour recaps
Select **12h Recap** to read the current cached recap for each selected feed. A recap card shows
the topic, creation time when available, and generated recap content.
The app currently exposes only the 12-hour view. The backend and public API accept additional
timeframe identifiers, but they are not selectable in the News interface and may not have cached
content for every topic.
See [AI news recaps](/docs/news/recaps) for interpretation and availability details.
## Refresh and stale data
The **Refresh** button re-runs the active Live Feed or Recap query. It does not start a new RSS or
X ingestion job. If the upstream cache has not changed, the same results remain visible.
Y2 displays a stale-data notice when the current query result is old. If no results appear:
1. Confirm that at least one feed is selected.
2. Try a broader set of feeds.
3. Select **Refresh** to re-read the cache.
4. If the cache is initializing, wait for the scheduled sync and try again.
## API access
The public API exposes cached signals, recaps, and the feed catalog:
Filter signals by topic and paginate results.
Retrieve cached topic recaps.
Discover available topic identifiers.
API-key requests require the `news:read` scope. New API keys require Pro or Elite; eligible
existing Lite keys retain their historical grace limits. These endpoints also support the x402
flow described in their generated references.
## Next steps
Review the complete feed catalog and topic identifiers.
Learn what the 12-hour synthesis contains and how to use it.
Configure an API key and scopes.
---
# AI News Recaps (/docs/news/recaps)
AI News Recaps condense recent signals for each selected topic into a cached narrative. Use them
to establish context quickly, then open the underlying Live Feed sources before making a
decision.
```mermaid
flowchart LR
A[Topic cache] --> B[12h window]
B --> C[Up to 24 recent signals]
C --> D{LLM recap available?}
D -->|Yes| E[Structured recap]
D -->|No or failed| F[Deterministic fallback]
E --> G[Recap cache]
F --> G
G --> H[News interface]
G --> I[Public API]
```
## Read recaps in the app
### Open News
Select **News** in the signed-in app.
### Select topics
Choose one or more feeds under **Global Signal Feeds**.
### Switch to 12h Recap
Select **12h Recap** in the page header. Each selected topic with cached content receives a
separate card.
### Verify important signals
Return to **Live Feed** and open attributed sources for any claim that affects your work.
The dashboard currently exposes only the 12-hour recap view. All 40 topics in the current feed
registry are configured for that window.
## Generation schedule
RSS ingestion starts at 00:00, 06:00, 12:00, and 18:00 UTC. Recap scheduling begins two minutes
after each of those cycles, then staggers topic jobs by 45 seconds to avoid starting all 40 at
once.
Recaps are pre-generated and cached. Opening the page or selecting **Refresh** re-reads the
cache; it does not generate a new recap on demand.
X ingestion currently runs on a separate weekly schedule. A recap can therefore combine recently
cached RSS signals with X signals captured during the latest X cycle.
## Recap contents
When structured generation succeeds, the cached record can contain:
The app prioritizes the `recap` narrative. The API returns the complete cached object for each
topic, so integrations should tolerate additional fields and the fallback shape.
### Fallback output
If structured LLM generation is unavailable or fails, Y2 creates a deterministic recap from the
recent cached signals. The fallback can include the narrative, item and source counts, source
mix, and top signals. This keeps the topic cache usable without presenting failed model output as
a successful structured result.
## Retrieve recaps with the API
```bash
curl "https://api.y2.dev/api/v1/news/recaps?topics=bitcoin,ethereum&timeframe=12h" \
-H "Authorization: Bearer y2_your_api_key"
```
The response maps each available topic ID to its cached recap:
```json
{
"data": {
"bitcoin": {
"recap": "Bitcoin 12-hour recap content...",
"executive_summary": "The highest-signal developments...",
"sentiment": "neutral",
"item_count": 12,
"created_at": "2026-07-21T12:18:00.000Z"
}
},
"meta": {
"timeframe": "12h",
"topics": ["bitcoin", "ethereum"]
}
}
```
The API schema accepts `12h`, `24h`, `3d`, and `7d`, but the current registry schedules only
`12h` cache entries. A valid longer timeframe can therefore return no recap for a topic.
See the generated [List recaps reference](/docs/api/reference/news/listRecaps) for the canonical
authentication, query, and response schema.
## Interpret recaps safely
A recap is generated from a bounded set of cached signals. It may omit events, repeat source
errors, or infer a misleading emphasis. It is not a substitute for source review or
professional financial, legal, security, or policy advice.
- A missing topic card can mean that no successful recap is cached for that selection.
- Quiet topics can produce a neutral “no direct signals” message.
- `item_count` describes the items considered by that recap, not total market activity.
- Sentiment summarizes the supplied signals; it does not forecast price or outcome.
- Source IDs in structured fields identify inputs, but you should use Live Feed source links for
direct verification.
## Next steps
Inspect individual signals and open their sources.
Choose feeds for the app or API.
Review authentication and the canonical response schema.
---
# Topics and Feeds (/docs/news/topics)
Y2 currently defines 40 News feeds in a shared source registry. The app and API use the IDs on
this page to select topic caches.
The feed catalog is configuration-driven. Use `GET /api/v1/news/feeds` when an integration
needs to discover the current list at runtime instead of storing a copy.
## Feed catalog
| Feed | ID | Coverage |
| --- | --- | --- |
| Geopolitics | `geopolitics` | Alliances, wars, sanctions, and diplomatic flashpoints |
| US Politics | `politics_us` | Federal politics, elections, Congress, and major policy shifts |
| Defense | `defense` | Military operations, NATO, weapons programs, and security policy |
| Sanctions & Trade | `sanctions_trade` | Export controls, tariffs, sanctions, and industrial policy |
| Feed | ID | Coverage |
| --- | --- | --- |
| Macro | `macro` | High-impact macroeconomic and geopolitical events with market relevance |
| Equities | `equities` | Global equity markets, earnings, indices, and market structure |
| Rates & FX | `rates_fx` | Interest rates, currencies, central banks, and yield curves |
| Commodities | `commodities` | Energy, metals, agriculture, and commodity-market shocks |
| Banking & Regulation | `banking_reg` | Banking stress, financial regulation, and financial stability |
| Feed | ID | Coverage |
| --- | --- | --- |
| Energy | `energy` | Oil and gas, power markets, grids, nuclear, and renewables |
| Cyber | `cyber` | Attacks, ransomware, vulnerabilities, infrastructure, and threat actors |
| Semiconductors | `semiconductors` | Chip manufacturing, foundries, accelerators, and supply chains |
| Biotech & Health | `biotech` | Pharma, biotech, health alerts, and life-science market events |
| Logistics & Maritime | `logistics` | Ports, shipping, chokepoints, supply chains, and disruptions |
| Critical Minerals | `critical_minerals` | Rare earths, lithium, copper, mining, and battery materials |
| Telecom | `telecom` | 5G, submarine cables, satellite broadband, and network infrastructure |
| Feed | ID | Coverage |
| --- | --- | --- |
| MENA | `region_mena` | Middle East and North Africa political, conflict, and economic signals |
| Europe | `region_europe` | European politics, EU policy, security, and regional economics |
| Asia-Pacific | `region_asiapac` | China, Indo-Pacific security, ASEAN, and regional political economy |
| Latin America | `region_latam` | Politics, security, crime, and economic signals in Latin America |
| Africa | `region_africa` | Politics, conflict, resources, and regional economic developments |
| North America | `region_namerica` | US, Canada, and Mexico cross-border political and economic signals |
| Feed | ID | Coverage |
| --- | --- | --- |
| AI | `ai` | LLMs, infrastructure, model launches, and major partnerships |
| AI Agents | `ai_agents` | Agent frameworks, incentives, ecosystems, and launches |
| Data | `dats` | Data, analytics, and infrastructure developments |
| Machine Learning | `machine_learning` | Academic, industry, and open-source ML developments |
| Tech | `tech` | Software, semiconductors, regulation, and AI infrastructure |
| Feed | ID | Coverage |
| --- | --- | --- |
| Base | `base` | Base L2 network and ecosystem developments |
| Bitcoin | `bitcoin` | Network upgrades, ETF flows, miners, and adoption |
| Crypto | `crypto` | Broad markets, launches, infrastructure, and protocols |
| DeFi | `defi` | Protocols, yield markets, governance, and infrastructure |
| Ethereum | `ethereum` | Protocol upgrades, scaling, validators, and ecosystem changes |
| Hyperliquid | `hyperliquid` | HYPE, governance, perps liquidity, and ecosystem projects |
| On-Chain Whales | `on_chain_whale` | Large transactions, wallets, and whale activity |
| Perps | `perps` | Perpetual futures, derivatives, leverage, and trading platforms |
| Ripple | `ripple` | XRP, XRPL, Ripple Labs, and related legal developments |
| RWA | `rwa` | Tokenized real-world assets, issuers, platforms, and regulation |
| Solana | `solana` | Protocol, validator, outage, and core-network developments |
| Token Listings | `token_listings` | Exchange additions, token listings, and launches |
| Virtuals | `virtuals` | Virtuals Protocol, GAME, agents, commerce, and tokenomics |
Every topic in the current registry uses a 12-hour recap window.
## Select feeds in the app
### Open News
Go to **News** and find **Global Signal Feeds**.
### Toggle topic chips
Select a chip to add that feed. Select it again to remove it. You can combine topics from
any group.
### Allow the preference to save
Feed changes are saved after a one-second debounce. The app restores the saved selection on
later visits.
The dashboard does not apply the API's default topic list to your saved preferences. If no feeds
are selected, choose at least one chip before the Live Feed or Recap view can load data.
## Choose a useful combination
Start narrow, then add broader feeds only when they contribute different coverage.
`macro`, `equities`, `rates_fx`, `geopolitics`
`ai`, `semiconductors`, `critical_minerals`, `energy`
`cyber`, `defense`, `telecom`, `geopolitics`
`crypto`, `bitcoin`, `ethereum`, `defi`
Broad and narrow topics can overlap. For example, `crypto` can include events also found under
`bitcoin` or `ethereum`. More feeds increase coverage and can also increase duplicate context.
## Use topic IDs with the API
Pass a comma-separated list to the `topics` query parameter:
```bash
curl "https://api.y2.dev/api/v1/news?topics=geopolitics,macro,energy&limit=50" \
-H "Authorization: Bearer y2_your_api_key"
```
If `topics` is omitted, the API currently uses:
```text
crypto, geopolitics, macro, equities, ai, energy
```
Unknown topic IDs return `400 Bad Request`.
### Discover feeds dynamically
```bash
curl "https://api.y2.dev/api/v1/news/feeds" \
-H "Authorization: Bearer y2_your_api_key"
```
Each feed entry includes its `id`, display names, description, group metadata, UI color, and
ontology-ingestion flag. Response metadata includes the catalog count and API default topics.
See the generated [List feeds reference](/docs/api/reference/news/listFeeds) for the canonical
response schema and authentication options.
## Next steps
Filter current signals and open attributed sources.
Understand recap availability and limitations.
Review pagination, filters, and response fields.
---
# Country Intelligence Cards (/docs/osint/country-cards)
Country Intelligence Cards combine the map's currently loaded signals with country-coded feeds,
regional instability data, prediction markets, market indices, and curated infrastructure. They
are available with the Situation Room on Lite, Pro, and Elite plans.
## Open a country card
### Open the Situation Room
Go to **OSINT → Monitor** and wait for the map to load.
### Choose the signal context
Enable the event layers you want to analyze and set the map time range. Those choices affect
the signals passed into the card.
### Select a country
Click or tap a country boundary. Y2 opens its card as a modal over the map.
### Close or export
Select **Close**, click the backdrop, or press `Escape` to close the card. Select the download
icon to save the currently assembled dossier as a UTF-8 text file.
Full centroid, baseline, infrastructure, market, and brief behavior is configured for the United
States, Russia, China, Ukraine, Iran, Israel, Taiwan, North Korea, Saudi Arabia, Turkey, the
United Kingdom, Germany, France, India, Pakistan, Syria, Yemen, Mexico, Colombia, Brazil,
Venezuela, Egypt, the United Arab Emirates, and South Korea. Other map boundaries can be
selected, but their card may contain only a header and empty or zero-value sections.
## How the card assembles data
```mermaid
flowchart LR
map["Current map state
Enabled layers · time range · loaded events"]
radius["Centroid filter
Events within 800 km"]
baseline["Curated profile
Baseline · default status · region"]
cii["Regional CII
Value · delta"]
code["ISO alpha-2 code"]
feeds["Country-coded queries
Signals · markets · stock"]
refs["Curated registries
Bases · nuclear · ports"]
card["Country card"]
map --> radius --> card
baseline --> card
cii --> card
code --> feeds --> card
refs --> card
```
The card does not perform polygon containment for its event widgets. It takes the events already
loaded for the map and keeps events whose coordinates are within **800 km of the curated country
centroid**. Events near a border can therefore appear in more than one card, while events in a
large country's outer regions can fall outside the radius.
The map query is capped and follows your enabled layers and selected time range. Active Signals,
the timeline, and the AI brief input describe that loaded subset—not an exhaustive national event
archive.
## Read the instability section
The ring is an equal-weight average of four 0–100 components:
| Component | What the card labels |
| --- | --- |
| Civil unrest | Political and social instability baseline |
| Conflict | Armed-conflict baseline |
| Security | Security-risk baseline |
| Information | Information-environment baseline |
For a profile mapped to a CII region, Y2 scales each static baseline component by the current
regional CII **delta**. It then averages the four adjusted components and rounds the result.
| Ring value | Ring color |
| --- | --- |
| 0–34 | Green |
| 35–64 | Amber |
| 65–100 | Red |
When regional CII exists, the `NORMAL`, `ELEVATED`, or `CRITICAL` badge uses the region's raw CII
value. The ring uses the profile baseline adjusted by the region's CII delta. Profiles without a
mapped region use their configured default status and unadjusted baseline. The badge and ring
color can therefore differ.
## Interpret signals and the timeline
**Active Signals** counts every event in the 800 km loaded subset and groups the results by event
category. It does not add a separate severity breakdown.
The **7-Day Timeline** further limits that subset to events from the last seven days and maps these
categories into four lanes:
| Lane | Included event categories |
| --- | --- |
| Protest | `political` |
| Conflict | `conflict` |
| Natural | `seismic`, `weather`, `fire` |
| Military | `maritime`, `aviation` |
Categories such as economic, health, and cyber can appear in Active Signals but not in a timeline
lane. If the map time range is set to 24 hours, the “7-Day” timeline can only show the 24-hour
subset it received.
## Read the intelligence brief
When no unexpired brief exists for the country, the card schedules an AI-generated brief. The
generation context contains:
- country name and two-letter code;
- the regional CII value, when available;
- the count of currently loaded events within 800 km;
- the first 10 titles from that same event subset.
The model is instructed to produce five sections in 300–400 words: current situation, military and
security posture, key risk factors, regional context, and outlook. It must acknowledge low activity
instead of inventing threats.
Briefs expire after two hours. Y2 records a context hash based on 10-point CII buckets and
five-event count buckets, but the card reads the newest unexpired brief for the country. A changed
map selection does not force a refresh while that brief remains valid.
The brief is generated from a small, bounded context. Validate important conclusions against the
linked source records before acting on them.
## Country-coded feeds
These widgets use the curated profile's ISO alpha-2 code instead of the 800 km radius:
### Latest signals
The card shows up to eight recent stored records tagged to the country from RSS, GDELT-compatible,
ACLED-compatible, URLhaus, and Feodo source types. Because threat-intelligence rows are eligible,
this section can contain more than conventional news headlines. Select a row to open its source URL
when one is available.
### Prediction markets
The card selects up to five unexpired Polymarket or Kalshi contracts whose derived country tags
include the selected code. Results are sorted by trading volume and show probability, volume,
optional liquidity and end date, and the provider link. A missing section means that no current
matching contract was found.
### Stock index
For 45 configured countries, the card requests a primary national index from Yahoo Finance when no
valid cache row exists. It displays the index name and percentage change between the latest close
and the close five trading observations earlier. Results expire after one hour. Unsupported codes
or provider failures leave the chip hidden.
## Infrastructure exposure
The card lists the **three nearest** records in each curated registry:
- military bases;
- nuclear facilities;
- strategic ports and chokepoints.
Distance is the great-circle distance from the country's configured centroid. The assets are not
filtered by political boundary, affiliation, or active map layers. “Exposure” therefore means
nearby reference context, not confirmed ownership or presence inside the selected country.
## Download the dossier
The download icon creates a file named
`y2-intel-{iso3}-{YYYY-MM-DD}.txt`. It includes the status, ring and component values, active-signal
counts, and any stock, brief, latest-signal, market, and infrastructure data already available when
you select download.
The export is a point-in-time client-side text snapshot. The current card does not provide a
separate PDF or print command.
## Keyboard shortcuts
| Key | Action |
| --- | --- |
| `Escape` | Close the country card |
| `L` | Toggle the map layer sidebar while focus is not in an input |
## Next steps
Configure the layers and time window that feed the card
Check source schedules, health, and retention
See how observations, specialized caches, and reference overlays differ
---
# Data Infrastructure (/docs/osint/data-infrastructure)
Y2 does not force every map layer into one universal record. It routes data according to its
shape, update rate, and useful lifetime. This distinction matters when you compare source health,
map freshness, search results, and API responses.
## Architecture at a glance
```mermaid
flowchart LR
sources["Scheduled providers
News · hazards · cyber · markets · tracking"]
adapters["Source adapters
Fetch · validate · normalize"]
health["Source control
Circuit state · last success · errors"]
observations["Observation store
Events with time, place, severity, and provenance"]
typed["Specialized caches
Aircraft · vessels · GPS · FININT · markets"]
ontology["Ontology
Sources · entities · places · incidents · relations"]
references["Reference overlays
Bases · cables · satellites · CCTV"]
products["Product views
Situation Room · country cards · search · API"]
sources --> adapters
adapters --> health
adapters --> observations
adapters --> typed
observations <--> ontology
typed --> products
observations --> products
ontology --> products
references --> products
```
The diagram shows three storage paths:
| Path | Best suited to | Examples |
| --- | --- | --- |
| Normalized observations | Time-bound events that can be searched, grouped, and linked | Earthquakes, fires, disaster alerts, threat indicators, news events, Y2-extracted signals |
| Specialized caches | Data with a provider-specific shape or refresh cycle | Aircraft tracks, vessel positions, GPS interference, prediction markets, FININT indicators |
| Reference overlays | Curated context rendered directly by the app | Military bases, nuclear facilities, undersea cables, satellite anchors, CCTV hubs |
A reference overlay can appear on the map without creating a normalized observation or a
source-health record. Its presence does not imply that a provider fetch just succeeded.
## From provider response to observation
### Check the source circuit
Before a scheduled adapter calls its provider, it checks that source's circuit-breaker state.
Five consecutive failures open the circuit for a five-minute cooldown. Other sources continue
independently.
### Normalize the provider payload
The adapter maps provider-specific fields to Y2 concepts such as source, title, category,
severity, event time, coordinates, country, URL, fetch time, and expiry. The original provider
payload may also be retained as source-specific metadata.
### Resolve ontology references
Where the adapter has enough context, Y2 resolves or creates the related data source, place,
entities, and incident. These links are optional: an observation can remain useful before all
ontology references are available.
### Upsert by source identity
Normalized observations use the pair `(sourceType, sourceId)` as their source identity. A later
fetch updates the active observation instead of blindly appending another row. Ambiguous legacy
ownership fails closed rather than overwriting an arbitrary record.
### Recompute dependent intelligence
Writes linked to an incident enqueue corroboration work. Separate scheduled jobs classify
observations, recompute conflict indicators, update statistical baselines, and reconcile stale
incidents and markets.
## Observation and ontology model
An observation is the evidence-bearing event record. It can carry:
- provider identity and raw metadata;
- event time, fetch time, and expiry;
- category, severity, coordinates, region, and country;
- provenance tier: `primary`, `derived`, or `synthesized`;
- temporal meaning: `ongoing`, `past`, `forecast`, or `analysis`;
- links to a canonical data source, place, entities, and incident.
The ontology supplies durable identity around those observations:
```mermaid
erDiagram
DATA_SOURCE ||--o{ OBSERVATION : produces
PLACE ||--o{ OBSERVATION : locates
INCIDENT ||--o{ OBSERVATION : groups
ENTITY }o--o{ OBSERVATION : appears_in
ENTITY ||--o{ ENTITY_RELATION : connects
OBSERVATION ||--o{ RELATION_EVIDENCE : supports
ENTITY_RELATION ||--o{ RELATION_EVIDENCE : has
```
Entity aliases, provider identifiers, and canonical names have dedicated lookup indexes. Merge
redirects preserve references when duplicate objects converge, while integrity jobs audit resolver
indexes and candidate duplicates. Automated destructive entity merging is intentionally
fail-closed until dependent references can be rewritten and verified.
### Identity keys and safe deduplication
Y2 keeps durable object identity separate from the text used to find an object. A Convex document
ID is the stable reference. Names, aliases, and provider identifiers are normalized into dedicated
lookup projections:
| Identity path | Indexed lookup |
| --- | --- |
| Canonical name | entity kind + normalized canonical key |
| Alias | entity kind + normalized alias key |
| Provider identifier | entity kind + provider + normalized provider value |
| Ownership | object type + identity namespace + claim key |
Normalization applies Unicode compatibility normalization, trims and collapses whitespace, and
compares case-insensitively while retaining the original display text. Candidate aliases are
deduplicated before resolution.
These are typed compound indexes, not application-encoded byte keys. Convex owns physical ordering;
Y2 chooses index fields in query order, uses indexed equality/range reads, and paginates large
partitions. Reverse-time queries use numeric timestamps and descending index order instead of
inverted timestamp strings. Opaque Convex IDs are never parsed for time or locality.
An index row is a rebuildable projection, not permission to choose an identity winner. Y2 can
automatically remove a dead, stale, kind-mismatched, or same-owner duplicate projection. If a
normalized key has multiple live owners, the resolver keeps both objects, skips the unsafe alias
transfer, and records one review case. A merge occurs only through the governed reference-rewrite
and verification process.
## Agent Y2 signals
Completed Y2 reports can be processed into structured observations with extraction context,
resolved entities, places, and incident links. Intel discovery and expansion jobs can create their
own observations through the same ontology layer.
These synthesized records support investigation graphs, evidence links, search, and other
intelligence views. They are not a promise that every future report automatically uses all prior
observations as research context.
## Freshness and retention
Freshness is controlled per data path rather than by one platform-wide timer.
| Data | Typical lifetime or lifecycle |
| --- | --- |
| Most normalized provider observations | Seven-day expiry, cleaned hourly |
| Aircraft tracks and GPS interference | 30 minutes |
| Naval vessel positions | 24 hours |
| Stock index cache | One hour |
| Prediction markets | Two-hour cache expiry, with separate market lifecycle reconciliation |
| FININT indicators | Provider-dependent, from two hours to 45 days |
| User Situation Room snapshots | 90 days |
| Inactive incidents | Automatically resolved after seven days without observations |
Y2 report, intel discovery, and intel expansion observations are excluded from the normal transient
observation cleanup because other evidence records can depend on them. Their own `expiresAt` value
does not make them subject to that hourly deletion path.
API and map queries return the records that remain in their active storage path. If you need a
durable external history, capture the relevant API results in your own governed store instead of
relying on a short-lived Y2 cache.
## How the app reads the data
The Situation Room combines several queries rather than loading one monolithic dataset:
- normalized observations are filtered by source, category, severity, geography, and event time;
- tracking and financial layers read their specialized tables;
- country views combine live calculations with curated country and infrastructure context;
- reference overlays are assembled from app registries and approved external display endpoints;
- the Sources page reads circuit-breaker records separately from map data.
This is why two surfaces can update at different times. For example, a source card can report a
healthy provider while a specialized layer is empty because its rows expired, and a static overlay
can remain visible while no runtime provider call exists for it.
## Next steps
Compare providers, schedules, health semantics, and retention
Learn how the map combines live, cached, and reference layers
Understand the country-level synthesis shown in the map
---
# Situation Room (/docs/osint)
The Situation Room is Y2's map-based workspace for combining current OSINT events, Y2 report
findings, network threats, reference infrastructure, tracking data, live media, and financial
indicators.
Situation Room access requires an active **Lite**, **Pro**, or **Elite** workspace. Free
workspaces see an upgrade wall. All three enabled plans currently receive the same 50-layer
selection limit and one-minute refresh entitlement.
```mermaid
flowchart LR
A[Scheduled source syncs] --> B[Y2 data store]
C[Y2 reports and signals] --> B
D[Reference datasets] --> E[Map overlays]
B --> E
E --> F[Markers and country cards]
B --> G[Theater and FININT panels]
B --> H[Ontology and API tools]
```
## Open the monitor
### Select Situation Room
Open **Situation Room** in the signed-in app. `/app/osint` redirects to the monitor at
`/app/osint/monitor` after Y2 verifies the active workspace's access.
### Choose a map view
Use the map controls to switch between the 3D globe and 2D Mercator map, and between the
default and satellite basemap. The monitor starts in the global globe view.
### Set the event window
Choose **1H**, **6H**, **24H**, **48H**, **7D**, or **ALL**. The default is 24 hours.
### Select layers
Use the rail on the left to toggle layer entries. Press `L` outside a text field to collapse
or expand the sidebar. New users start with every registered entry selected, and later changes
are saved to their map preferences.
### Inspect the map
Select an event or tracking marker for its source and context. Select a country polygon to
open the country intelligence card. Press Escape to close the selected country.
## Understand the map controls
### Time range
The time bar changes the minimum timestamp sent to the cached map-event query. It applies to event
markers such as earthquakes, conflicts, weather, fires, and Y2 report events. Static reference
overlays—such as cables, bases, facilities, ports, cameras, and satellite assets—do not become
historical merely because the event window changes. Live tracking tables also use their own
freshness and expiry rules.
### Regions and theaters
The header includes **Global**, six geographic theaters, and two thematic views:
| Type | Views |
| --- | --- |
| Geographic | MENA, Africa, Latin America, Asia-Pacific, Europe, North America |
| Thematic | Energy & Resources, Government / Policy |
Selecting a header view moves the camera to its configured viewport. It does not replace the
active layer selection. The desktop Theater Intel dock separately groups recent events into the
same eight theater definitions.
### Location and cursor context
The desktop header can request browser geolocation and center the map on the result. It also shows
the current cursor coordinates. Y2 uses marker, cursor, theater, and time-window context to prepare
queries in the API tool; it does not send browser location until you request it.
## Layer registry
The current registry contains 27 selectable entries across seven groups.
**Y2 Report Events** maps event observations extracted from generated Y2 reports and preserves
report/profile provenance when available.
**Earthquakes**, **Conflicts**, **Political Events**, **Economic Events**, **Weather & Climate**,
**Health & Pandemic**, **Cyber Threats**, **Maritime Events**, **Satellite Fires**, and
**Military Aviation** display categorized rows from the OSINT event cache.
**Live Malware** adds recent URLhaus and Feodo-derived threat observations and connects them to
the Network Intel panel and relationship graph.
**All Satellites** is the aggregate control. Its five subtype entries are **Starlink / Comms**,
**Military / Intel**, **GPS / Navigation**, **Earth Observation**, and **Stations / Telescopes**.
**CCTV Cameras** displays configured public camera assets. Selecting a marker opens the camera
viewer; individual streams can still be unavailable at their upstream source.
**Undersea Cables**, **Nuclear Facilities**, and **Ports & Chokepoints** are reference overlays.
They have minimum zoom thresholds, so zoom in when a selected layer appears empty.
**Aircraft Tracks**, **Naval Vessels**, **GPS Interference**, **Conflict Zones**, and **Military
Bases** combine tracking tables with static reference overlays.
“All Satellites” and its five subtypes are related controls. The 27 count describes selectable
registry entries, not 27 independent upstream feeds.
When every entry is enabled, the map still may not show a marker for every layer. A source can have
no current rows, a row can fall outside the selected time window, a reference layer can require a
closer zoom, or an upstream integration can be unavailable.
## Current tracking status
The tracking status widget appears only when at least one tracking source has usable data or a
recorded aircraft sync timestamp.
| Layer | Current ingestion state |
| --- | --- |
| Aircraft Tracks | The Wingbits aircraft cron has been disabled since May 8, 2026 to stay within the shared request budget. The layer remains registered but automatic aircraft scans are not running. |
| GPS Interference | Wingbits GPS zones run on cadence tiers ranging from hourly hot zones to a daily verification sweep. |
| Naval Vessels | USNI fleet data syncs daily; the upstream fleet report itself updates less frequently. |
Do not interpret an empty Aircraft Tracks layer as proof that no aircraft are present. It
currently reflects disabled scheduled ingestion, not a negative intelligence finding.
## Country cards
Select a country to open its intelligence card. Depending on available data, the card combines:
- A country instability index and recent change.
- An AI-generated or cached country brief.
- A seven-day event timeline and active signals.
- Infrastructure exposure from reference datasets.
- Related headlines, prediction markets, and a stock-index indicator.
- A Markdown export assembled in the browser.
Country cards combine static country profiles with live queries, so one section can have data while
another is empty. See [Country Intelligence Cards](/docs/osint/country-cards) for field and scoring
details.
## Desktop analysis panels
The desktop monitor adds controls that are intentionally hidden on screens below the medium
breakpoint.
### Right-side tool rail
Lists countries or regions whose computed instability index is rising, sorted by delta.
Explores recent malware infrastructure and can open related indicators, entities, and network
context.
Searches ontology entities after two characters and opens a live relationship graph for a
selected result.
Builds OSINT area, signal, and report requests from the selected marker, cursor, region, and
time range.
The API Query tool produces URLs and `curl` snippets. It does not bypass authentication. Creating
new API keys and using scoped REST access requires Pro or Elite even though Lite can use the
in-app Situation Room.
### Bottom dock
The resizable desktop dock contains:
- **Live media:** eight configured YouTube news channels, loaded muted by default.
- **Theater Intel:** recent cached events grouped across six geographic and two thematic panels.
- **FININT:** market, energy, and macro indicators with recent values and detected signals.
These panels are not rendered on mobile; the map, layer selector, time range, markers, tracking
status, cameras, and country card remain the primary mobile surfaces.
## Data and source health
Map data is a mixture of scheduled third-party syncs, Y2-derived observations, typed intelligence
tables, and versioned static reference assets. “Live” means the interface updates reactively when
stored data changes; it does not mean every upstream provider streams continuously or has the same
refresh interval.
Use **Sources** in the monitor header to inspect the source registry, last-success state, record
counts, errors, and dataset-specific notes. See [Intelligence Sources](/docs/osint/sources) and
[Data Infrastructure](/docs/osint/data-infrastructure) before treating freshness across layers as
uniform.
## Entitlements and unfinished management surfaces
Lite, Pro, and Elite currently share these Situation Room entitlement values:
| Limit | Value |
| --- | ---: |
| Selected map layers | 50 |
| Alert rules | 100 |
| Countries per watchlist | 50 |
| Refresh interval | 1 minute |
| Snapshot retention | 90 days |
The backend includes authenticated mutations for watchlists and alert rules and enforces these
limits. The current Situation Room monitor does not expose a watchlist or alert-rule management
screen, so do not rely on the quota table as evidence that those app workflows are available.
Elite additionally enables organization workspaces, members, and audit logs. Review
[Plans and Limits](/docs/plans) for the surrounding profile, API, and workspace differences.
## Next steps
Identify which providers and datasets back each monitor surface.
Follow ingestion, normalization, identity, and API representations.
Interpret country-level scores, events, markets, and exports.
Use scoped REST endpoints outside the monitor.
Compare Y2 with Maltego, Flashpoint, Recorded Future, Palantir, and MISP by use case.
---
# Intelligence Sources (/docs/osint/sources)
The Situation Room combines scheduled upstream feeds, event-driven Y2 observations, specialized
tracking and market tables, and curated reference overlays. These source classes have different
refresh and health semantics; a layer name does not imply a continuously streaming provider.
```mermaid
flowchart LR
A[Scheduled provider] --> B[Source adapter]
C[Y2 workflow] --> D[Internal observation]
B --> E[Normalized storage]
D --> E
F[Curated reference asset] --> G[Map overlay]
E --> H[Map, cards, panels, API]
G --> H
B --> I[Circuit-breaker status]
```
## Inspect source status
### Open Situation Room
Open the monitor, then select **Sources** in the header. The page requires the same Lite,
Pro, or Elite Situation Room access as the monitor.
### Identify the source class
**Runtime feeds** display circuit-breaker state. **Map overlays and reference networks** are
catalog entries and do not have the same fetch-health record.
### Read the timestamps and errors
Runtime cards show the last successful fetch, consecutive failure count, current state, last
error when present, and related map-layer IDs.
### Confirm source-specific freshness
Compare the card with the schedules below. A closed circuit means requests are allowed; it
does not prove that every row is recent or that every endpoint inside an aggregate source
succeeded.
## Scheduled upstream feeds
The following jobs are registered in the current Convex cron configuration.
| Source | Stored data | Current schedule |
| --- | --- | --- |
| USGS | Earthquakes | Twice hourly at :05 and :35 |
| GDACS | Global disaster alerts | Twice hourly at :05 and :35 |
| NASA EONET | Natural-event observations | Twice hourly at :05 and :35 |
| OSINT RSS | Categorized headlines from 24 feeds | Twice hourly at :07 and :37 |
| NASA FIRMS | Active fire detections for configured regions | Twice hourly at :10 and :40 |
| Polymarket | Prediction markets | Every 15 minutes |
| Kalshi | Prediction markets | Twice hourly at :24 and :54 |
| URLhaus and Feodo Tracker | Malware URLs and command-and-control infrastructure | Twice hourly at :12 and :42 through a serialized work pool |
| NVD | CVE changes | Every 6 hours at :15 |
| CISA KEV | Known Exploited Vulnerabilities catalog | Daily at 04:20 UTC |
| FRED | Macroeconomic indicators | Twice hourly at :14 and :44 |
| Yahoo Finance markets | Market, commodity, FX, crypto, rate, and volatility indicators | Every 15 minutes at :04, :19, :34, and :49 |
| EIA spot series | WTI, Brent, and Henry Hub series | Hourly at :21 |
| EIA release series | Weekly and monthly energy series | Daily at 14:30 UTC |
| EIA nuclear outages | Facility outage snapshot | Daily at 14:45 UTC |
| EIA grid load | Six US balancing-authority series | Hourly at :33 |
| EIA crude imports | Per-port monthly data | Monthly on day 1 at 15:45 UTC |
| USNI Fleet Report | Naval vessel disposition | Daily at 06:20 UTC |
Schedules describe when Y2 attempts a fetch. The upstream dataset can update more slowly, return
no rows, rate-limit a request, or be unavailable.
## Hazards and earth observation
The adapter reads the USGS earthquake feed for global magnitude 2.5+ observations and stores
magnitude, depth, coordinates, time, and the provider's tsunami indicator. Category and
severity normalization drive the Earthquakes layer.
GDACS contributes global disaster alerts such as earthquakes, cyclones, floods, volcanic
events, and drought-related observations. Provider alert context is normalized into Y2 event
fields.
EONET contributes earth-observation events such as storms, fires, volcanic activity, and
other natural phenomena. Rows can feed the weather and fire views when coordinates and
categories are available.
FIRMS fetches active thermal anomalies for nine configured regional bounding boxes. The
adapter requests a two-day lookback and caps each regional result set before storage.
Thermal detections are observations, not proof of conflict activity or cause.
## OSINT RSS headlines
The OSINT map pipeline has its own 24-feed registry, separate from the 40-topic News Signal Feed.
It includes:
- Al Jazeera, Middle East Eye, and Al Monitor
- BBC Africa and AllAfrica
- BBC Latin America, InSight Crime, Mexico News Daily, and Reuters Americas
- BBC Asia and South China Morning Post
- BBC Europe and EUobserver
- BBC World
- New York Times World
- Reuters World
- OilPrice
- BleepingComputer and The Hacker News
- WHO News, WHO Disease Outbreaks, ProMED-Mail, ReliefWeb Health, and CDC MMWR
The adapter infers category and severity from source defaults plus headline and description
keywords. It also attempts place and incident resolution only for items that have coordinates.
Feed requests run in batches of five with a ten-second timeout per feed.
The Sources page reports one aggregate `rss` circuit. A sync is treated as successful when at
least one of the 24 feeds succeeds, so a closed RSS circuit does not guarantee that every feed
was available during the last run.
## Cyber intelligence
Y2 stores multiple cyber source types with different cadences and downstream representations.
| Source | What it contributes | Important boundary |
| --- | --- | --- |
| URLhaus | Active malware distribution URLs and related host indicators | Each sync is capped by the adapter and resolved into ontology entities when possible |
| Feodo Tracker | Botnet command-and-control IP or host infrastructure | Shares the serialized cyber ingestion work pool with URLhaus |
| NVD | Vulnerability records updated in the last-modified window | Six-hour delta ingestion; it is not the same as a full NVD backfill |
| CISA KEV | Vulnerabilities known to be exploited | Daily catalog sync; KEV status is an exploitation signal, not a severity score by itself |
URLhaus and Feodo observations power the Live Malware and network-intelligence surfaces. NVD and
CISA KEV also feed the broader Cyber experience and ontology. Free workspaces can use the in-app
Cyber surface, while Situation Room access still requires Lite or higher. New scoped API keys
require Pro or Elite.
## Markets and financial intelligence
### Prediction markets
Polymarket and Kalshi records are normalized into the prediction-market table and also written to
the ontology market model when subject entities can be resolved. Market questions are associated
with countries using provider metadata and configured text matching.
A market probability is a market price, not a verified forecast or Y2 confidence score. Review
liquidity, resolution terms, end date, and source URL before using it as evidence.
### FININT feeds
The desktop FININT panel groups normalized indicators into Markets, Energy, and Macro segments.
FRED, the scheduled Yahoo Finance market adapter, and the EIA family use the cadences in the table
above.
Country cards use a separate Yahoo Finance index adapter. When requested, it fetches the configured
primary index for the selected country, calculates an approximately five-trading-day change, and
caches that result for one hour. That on-demand country-index lookup does not have its own recurring
cron.
In-app FININT is available in Lite, Pro, and Elite. New `intel:finint` API keys require Pro or
Elite; existing Lite keys can retain compatibility limits while the workspace remains on Lite.
## Military and navigation tracking
### Aircraft Tracks
The Wingbits aircraft adapter and map layer remain in the repository, but scheduled aircraft
scans have been disabled since May 8, 2026. Re-enabling requires both cron entries and the
`WINGBITS_AIRCRAFT_TRACKING_ENABLED` environment flag.
The `wingbits` source-health card is shared with GPS-interference ingestion. It can look healthy
because GPS jobs are succeeding even though automatic aircraft tracking remains disabled.
### GPS Interference
Wingbits navigation-accuracy observations are stored as H3 zones with sample count, aircraft
count, average navigation accuracy, and normalized severity. The 22 configured theaters use four
cadence tiers:
| Tier | Cadence | Coverage count |
| --- | --- | ---: |
| Hot | Hourly | 6 theaters |
| Watch | Every 3 hours | 8 theaters |
| Perimeter | Every 6 hours | 5 theaters |
| Verification | Daily | 3 theaters |
GPS zones expire after 30 minutes. A lower-cadence theater can therefore have no current map row
between scans; absence is not evidence that interference stopped.
### Naval Vessels
The USNI adapter ingests fleet-report-derived vessel positions once daily. Stored rows expire
after 24 hours and include vessel class, status, region, strike-group context when present, and a
confidence label. The upstream report itself is not a live AIS stream.
## Internal and recognized source types
The Sources screen also lists identifiers that are not independent scheduled provider feeds:
`y2_report` observations are created from generated Y2 reports and signals. They preserve
report and profile context where available and power the Y2 Report Events layer.
`intel_discovery` and `intel_expansion` observations are created by Y2 intelligence workflows.
They represent derived platform activity rather than third-party source polling.
`manual` identifies operator-entered rows. Its presence in the source-type schema does not
create an ingestion schedule.
`acled`, `gdelt`, and `opensky` remain accepted source identifiers and appear in the in-app
catalog. The current cron file has no scheduled ACLED, GDELT, or OpenSky ingestion job. Treat
their cards as compatibility or planned-source descriptors unless a runtime status row proves
that another in-scope process populated them.
## Reference overlays and media
Reference cards do not use runtime circuit-breaker health. They describe data bundled into or
referenced by the frontend:
- Curated satellite category anchors for the current Space toggles. CelesTrak and SatNOGS are
listed as candidate/reference catalogs; the map is not presenting live orbital propagation from
those providers.
- Curated undersea-cable routes with Submarine Cable Map lineage.
- Y2-maintained strategic ports, military bases, and nuclear-facility inventories.
- Configured public CCTV network assets.
- Eight YouTube live-news channels used by the desktop media panel.
Reference overlays can age independently of runtime feed health. Validate important facility,
route, ownership, and status details against the relevant primary source before operational use.
## Normalization and retention
### Check the circuit
A scheduled adapter first asks whether its source circuit allows a request.
### Fetch and normalize
Source-specific code maps provider fields to typed observations or specialized tracking,
market, vulnerability, and FININT rows. Primary adapters attach their registered data-source ID
and provenance tier when available.
### Resolve context
Where supported, Y2 resolves places, entities, incidents, market subjects, and evidence links.
Resolution failure can be non-fatal, leaving the source observation available without every
ontology link.
### Upsert deterministically
Event-cache rows use the `(sourceType, sourceId)` identity. Duplicate items within a batch
collapse with the last value winning; existing rows are updated rather than blindly appended.
### Publish reactively
Convex queries update monitor markers, cards, panels, and app tools when stored data changes.
Transient `osintDataCache` observations normally receive a seven-day expiry. Evidence-bearing Y2
report, discovery, and expansion observations are excluded from the transient cleanup set.
Specialized tables have independent retention—for example, GPS and aircraft rows use 30 minutes,
vessels use 24 hours, and the country stock-index cache uses one hour.
## Interpret circuit-breaker state
| State | Meaning in the current UI |
| --- | --- |
| Pending | No circuit-breaker row exists for that source type; often means it has never recorded a fetch result |
| Closed | Requests are allowed. The card can still show 1–4 accumulated failures before the threshold opens the circuit |
| Open | Five or more consecutive failures have opened the circuit; requests are skipped until the five-minute cooldown expires |
| Half open | A probe state recognized by the schema and UI; the next successful result closes it and a failed probe reopens it |
After cooldown, an open source is allowed to attempt another request. A success resets the failure
count and last error; another failure keeps the circuit open.
Source health describes adapter execution, not truthfulness, completeness, or corroboration of
the underlying data. Always retain the source URL and provenance when making a consequential
assessment.
## Next steps
Relate each source class to its current map or analysis surface.
Follow identity, ontology, evidence, and API representations after ingestion.
See how source records combine at country level.
Query supported public representations with scoped credentials.
---
# Plans and Limits (/docs/plans)
Y2 resolves feature access and quotas from the active workspace. Switching workspaces can change
the features available in the app and API even when the same person is signed in.
Personal and organization workspaces have separate plans, resources, API keys, and usage.
Confirm the active workspace before troubleshooting a missing feature or unexpected limit.
## Pricing and workspace model
| Plan | Price | Workspace | Best fit |
|---|---:|---|---|
| Free | Free | Personal, 1 member | Follow one community profile |
| Lite | $5/month or $50/year | Personal, 1 member | Ongoing in-app intelligence and webhook delivery |
| Pro | $20/month or $200/year | Personal, 1 member | Scheduled delivery, API integrations, audio, and advanced publishing |
| Elite | $500/month or $5,000/year | Organization, up to 5 members | Shared intelligence operations with governance controls |
## Projects and Automations
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Private Projects | No | Yes | Yes | Yes |
| Project graph, timeline, evidence, and knowledge | No | Yes | Yes | Yes |
| Automations | No | No | Up to 10 | Up to 50 |
| Schedule and profile-completion triggers | No | No | Yes | Yes |
| Project-bound Automation output | No | No | Yes | Yes |
| User-bound Project and Automation audit | No | No | No | Yes |
Project access starts with Lite. Automations require Pro or Elite, and their definition quota is
shared by the workspace. Projects and Automation definitions are owner-private in this release;
an Elite organization does not automatically share one member's Project content with another.
## Profiles and delivery
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Custom profiles | 0 | 1 | Up to 20 total profiles | Up to 50 total profiles |
| Community subscriptions | 1 | 3 | Up to 20 total profiles | Up to 50 total profiles |
| Total profiles | 1 | 4 | 20 | 50 |
| Report deliveries | Community subscription only | Unlimited | Unlimited | Unlimited |
| Email delivery | Yes | Yes | Yes | Yes |
| Webhook delivery | No | Yes | Yes | Yes |
| SMS delivery | No | No | Yes | Yes |
| Scheduled reports dashboard | No | No | Yes | Yes |
| Branding templates | 0 | 0 | 10 | 25 |
| Audio narration | No | No | Yes | Yes |
Free includes one community-profile subscription. Creating a custom profile and configuring its
report delivery starts with Lite.
## Agent Y2 and research
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Agent Y2 credits per month | 0 | 100 | 1,000 | 5,000 |
| Web search tool | No | Yes | Yes | Yes |
| Image generation | No | Yes | Yes | Yes |
| Advanced research | No | Yes | Yes | Yes |
| News Terminal | Yes | Yes | Yes | Yes |
## API access
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Create new API keys | No | No | Up to 5 | Up to 25 |
| Default rate limit per minute | — | — | 30 | 120 |
| Default rate limit per day | — | — | 5,000 | 50,000 |
| Webhook management | No | Yes | Yes | Yes |
| x402 pay-per-request | Supported endpoints | Supported endpoints | Supported endpoints | Supported endpoints |
Lite cannot create new API keys. Keys that existed before the Pro requirement retain a
compatibility limit of 10 requests per minute and 500 requests per day while the workspace
remains on Lite.
## Situation Room and Intel workspaces
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Cyber dashboard | Yes | Yes | Yes | Yes |
| Situation Room | No | Yes | Yes | Yes |
| FININT workspace | No | Yes | Yes | Yes |
| Explorer investigation boards | No | Yes | Yes | Yes |
| Investigation sharing | No | No | Yes | Yes |
| Map layers | 0 | 50 | 50 | 50 |
| Alert rules | 0 | 100 | 100 | 100 |
| Watchlist countries | 0 | 50 | 50 | 50 |
| Snapshot retention | — | 90 days | 90 days | 90 days |
## Organization capabilities
| Capability | Free | Lite | Pro | Elite |
|---|---:|---:|---:|---:|
| Organization workspace | No | No | No | Yes |
| Workspace members | 1 | 1 | 1 | Up to 5 |
| Workspace audit logs | No | No | No | Yes |
| Workspace API-key oversight | No | No | No | Yes |
## Choose the next guide
Set up a topic, cadence, and delivery path within the active workspace limits.
Deliver reports to an HTTPS endpoint on Lite, Pro, or Elite.
Configure Pro or Elite credentials and scopes for API requests.
Understand workspace switching, resource ownership, and Elite team governance.
Organize chats, evidence, private knowledge, graphs, and reports on Lite or higher.
Run bounded Agent Y2 work from Pro and Elite workspace triggers.
---
# Branding Templates (/docs/profiles/branding)
Branding templates let a Pro or Elite workspace reuse its logo, colors, terminology, and
delivery copy across intelligence profiles.
Branding requires an active **Pro** or **Elite** workspace. Pro supports up to **10**
templates; Elite supports up to **25**.
```mermaid
flowchart LR
A[Active workspace] --> B[Branding template]
B --> C[Assigned profile]
C --> D[Report]
C --> E[Email]
C --> F[SMS]
C --> G[Audio]
```
## Create and assign a template
### Open Branding
In the app, go to **InfoOps → Branding**, then select **New Template**.
### Name the template
Use a name that identifies where the branding belongs, such as “Company Briefings” or
“Client Deliverables.” A template name can contain up to 50 characters.
You can also mark the template as the workspace default. Only one template can hold that
marker at a time.
### Configure the output
Add only the elements you need. Any omitted values fall back to Y2 defaults.
| Area | Available settings |
| --- | --- |
| Visual identity | Logo, logo alt text, header color, accent color |
| Terminology | Report type and analyst role |
| Delivery copy | Email preview prefix, SMS prefix, audio intro, audio outro |
| Static content | Optional content before or after a report |
### Save the template
Resolve any validation errors, then select **Save**.
### Assign it to a profile
Create or edit a profile, open **Advanced**, select the branding template, and save the
profile. Branding is applied when that profile is rendered or delivered.
The **Set as Default** marker is unique, but the current profile editor still requires you to
select a branding template explicitly. Verify the assignment in **Advanced** instead of
relying on the default marker alone.
## Visual identity
### Logo
The branding editor accepts PNG, JPEG, SVG, and WebP files up to 2 MB. Add meaningful alt text
for recipients who cannot see the image.
| Property | Enforced limit |
| --- | --- |
| File size | 2 MB |
| Logo alt text | 100 characters |
Use a wide logo with a transparent background when possible, then preview a delivered email to
confirm that it remains legible at small sizes.
### Colors
Header and accent colors accept three- or six-digit hex values, such as `#05c` or `#0055cc`.
Values are normalized when the template is saved.
Use only hex colors. Invalid values are rejected by server-side validation.
## Terminology and delivery copy
Use `{name}` in supported text fields to insert the assigned profile's name.
For pronunciation rules that apply to names, acronyms, or technical terms, use a
[pronunciation dictionary](/docs/audio#create-a-pronunciation-dictionary).
## Add static content
Each template can place one content block before the report and another after it. A block can
use the `callout`, `highlight`, or `sponsor` style and can contain channel-specific copy.
| Content | Enforced limit | Used by |
| --- | ---: | --- |
| Label | 30 characters | Block heading |
| HTML | 500 characters | Email and rendered report |
| Audio text | 200 characters | Audio narration |
The `{name}` placeholder is supported in the label, HTML, and audio text.
Server-side limits control what can be saved. Keep static HTML at or below 500 characters,
audio text at or below 200, and labels at or below 30 even if an editor counter displays a
larger allowance.
Static HTML is sanitized before it is stored and rendered. Scripts, embedded forms, event
handlers, unsafe URLs, iframes, and similar executable elements are removed. Use simple
formatting and links rather than interactive markup.
## Update or remove a template
Assigned profiles reference the template rather than copying it. Changes therefore affect
subsequent renders and deliveries from every profile using that template.
To delete a template:
### Find assigned profiles
Edit each profile that uses the template.
### Remove or replace the assignment
Select another template or clear the branding selection, then save the profile.
### Delete the template
Return to **InfoOps → Branding**, select **Delete**, and confirm.
The backend rejects deletion while any profile still references the template. Unassign every
profile first, even if the confirmation dialog suggests that profiles will fall back
automatically.
## API boundary
The public profile create and update schemas expose `brandingTemplateId`. Use the generated
[Profiles API reference](/docs/api/reference/profiles) for the canonical request schema and
required scopes.
Creating, editing, and deleting branding templates is currently an app-only workflow; dedicated
branding-management endpoints are not part of the public OpenAPI document.
## Plan limits
| Plan | Templates | Logo upload |
| --- | ---: | --- |
| Free | 0 | No |
| Lite | 0 | No |
| Pro | 10 | Yes |
| Elite | 25 | Yes |
## Next steps
Configure a profile and assign the template.
Review how generated reports reach recipients.
Compare workspace capabilities and quotas.
---
# Create a Profile (/docs/profiles/creating)
Use the profile editor to define a recurring intelligence question, structure its reports, and
configure how Y2 researches and schedules it.
Sign in, complete onboarding, and select the workspace that should own the profile. Profile
capacity belongs to the active workspace; see [Plans and limits](/docs/plans).
## Editor workflow
```mermaid
flowchart LR
basic["1. Basic information"] --> structure["2. Report structure"]
structure --> advanced["3. Advanced configuration"]
advanced --> create["Create feed"]
create --> delivery["Review delivery settings"]
```
The editor validates each step before moving forward. On desktop, the report preview updates as
you change the profile.
## Create the profile
### Open the editor
Open [My Profiles](/app/infoops/my-profiles), then select **Create Profile**. You can also open
the [new profile editor](/app/infoops/profiles/new) directly.
### Complete Basic Information
Enter the fields shown in the first editor step:
Write the topic as a monitoring objective rather than a category name:
```text
Monitor newly disclosed ransomware campaigns affecting US healthcare providers,
including exploited vulnerabilities, named threat actors, and operational impact.
```
The editor may display a three-layer option, but report execution currently caps recursion
at the root topic plus one child layer. Select the two-layer option for the deepest effective
workflow.
### Design the report structure
Choose a **General**, **Finance**, **Technology**, **Security**, or **Custom** template. Reorder,
rename, add, or remove sections until the preview matches the decision the report should
support.
Add **Custom Instructions** when the topic alone does not express the required emphasis, tone,
comparison, or exclusions.
```text
Separate confirmed exploitation from unverified reporting. Prioritize patient-care impact,
affected vendors, and mitigations with named owners or deadlines.
```
Switching from a custom section list to a preset replaces the custom sections after
confirmation.
### Configure schedule and research
In **Advanced Configuration**, review these settings:
Choose **Daily**, **Weekly**, **Biweekly**, or **Monthly**, then select the required day and
time. The editor stores and displays schedule times in UTC.
Choose Basic or Advanced search, select a time range from one day to one year, and
optionally add comma-separated domains to include or exclude.
**Auto** lets the model decide when to use its active research tool. **Required** requires
tool use. **None** disables tool calls for the run.
Source Freshness applies the current recency preference and maximum-age settings. Link
Validation checks whether selected links are accessible.
Pro and Elite can enable audio narration, choose a voice and speed, apply a pronunciation
dictionary, and select a branding template. Unavailable controls remain disabled on lower
plans.
Turn **Share with Community** off when the profile should remain workspace-only. When it is
on, other users can discover and subscribe to the profile.
### Create and verify the profile
Select **Create Feed**. Y2 creates the profile as active, schedules its next run, creates the
workspace's subscription record, and returns you to **My Profiles**.
Confirm that:
- The profile appears in **My Profiles**.
- Its name, topic, and cadence match the editor.
- Its status is active.
- The profile opens without an access or plan-limit error.
### Review delivery after creation
Open the saved profile's delivery settings. New profile subscriptions use email when no other
method is selected. Webhooks require Lite or higher; SMS requires Pro or Elite and a verified
phone number.
Delivery preferences are tied to the subscription, so they become available after the profile
exists.
## Example configurations
```yaml
name: Weekly Cyber Risk Brief
topic: >-
Monitor ransomware campaigns affecting US healthcare providers, including exploited
vulnerabilities, threat actors, and operational impact.
tags: [cyber, ransomware, healthcare]
template: Security
frequency: Weekly
researchLayers: 2
timeRange: Week
```
```yaml
name: Critical Supplier Risk Monitor
topic: >-
Monitor disruption risks affecting semiconductor suppliers, East Asia ports, rare-earth
inputs, and export-controlled components.
tags: [supply-chain, semiconductors, logistics]
template: General
frequency: Daily
researchLayers: 2
includeDomains: [reuters.com, cisa.gov]
```
## Troubleshooting
Make the name at least three characters and the topic at least ten characters. Validation
messages appear below the affected field.
Confirm the active workspace and compare its current profile count with
[Plans and limits](/docs/plans). Limits include owned profiles and subscriptions.
Confirm that the active workspace includes the feature. Audio, branding, and SMS require Pro
or Elite; webhook delivery starts with Lite.
Narrow the topic to a geography, industry, entity set, time horizon, or decision. Use Custom
Instructions for output constraints and domain controls for source boundaries.
## Next steps
Understand UTC timing, frequency-specific fields, and schedule changes.
Verify the default delivery path and report format.
Deliver completed reports to an HTTPS endpoint.
Review visibility, subscriber behavior, and ownership before publishing a profile.
---
# Information Operations (/docs/profiles)
An Information Operations profile is the durable configuration behind a recurring Y2 briefing. It
defines what to research, how to structure the result, when to run, and where the report should go.
## Profile lifecycle
```mermaid
flowchart LR
config["Topic, instructions, and research settings"] --> run["Research workflow"]
run --> report["Structured intelligence report"]
report --> workspace["Y2 workspace"]
report --> delivery["Enabled delivery channels"]
report -. "context for later cycles" .-> run
```
The profile persists between runs. Updating its topic, instructions, source-quality settings, or
cadence changes later reports without requiring a new workflow each time.
## What a profile controls
A topic, tags, and optional custom instructions define the question and desired emphasis.
A template or custom section list defines how the result is organized, including a required
lead section such as Bottom Line Up Front.
Search depth, time range, domain controls, source freshness, link validation, and recursive
research tune how evidence is gathered.
Daily, weekly, biweekly, or monthly timing controls when the profile runs. The active plan
determines which delivery methods are available.
## Profile types
Subscribing to a global or community profile does not transfer ownership. The original owner keeps
the profile configuration; the subscriber controls their own subscription and supported delivery
preferences.
Profile ownership and subscriptions count against the active workspace. Free includes one
community-profile subscription but no custom profiles. Creating a custom profile starts with
Lite. See
[Plans and limits](/docs/plans) for the full entitlement reference.
## Research depth and source quality
Y2 supports a single-pass workflow and recursive research. When recursion is enabled, the runtime
can identify subtopics and research them in parallel before final synthesis.
Report generation currently caps recursive depth at one child layer: the root topic plus one
layer of parallel subtopic research. A larger saved value does not create additional runtime
layers.
Source-quality settings can prefer recent results, apply a maximum source age, and validate links.
These controls improve evidence selection, but they do not guarantee that an external source is
complete or correct.
## Report structure
Profiles can use a general, finance, technology, or security-oriented structure, or a custom list
of sections. The first required section establishes the main conclusion; later sections add
findings, analysis, risks, outlook, or other domain-specific context.
```mermaid
flowchart TD
lead["Required lead section"] --> findings["Key findings or evidence"]
findings --> analysis["Analysis and implications"]
analysis --> outlook["Outlook or next indicators"]
```
Audio narration and pronunciation dictionaries are separate Pro and Elite capabilities. Branding
templates can apply a consistent visual identity and terminology to supported reports.
## Choose the next task
Configure a topic, report structure, research behavior, cadence, and initial delivery.
Understand frequencies, UTC scheduling, and changes to future runs.
Make an eligible personal profile discoverable to the community.
Apply reusable visual identity and report terminology on supported plans.
Choose narration voices and pronunciation dictionaries for generated reports.
---
# Schedule Profile Reports (/docs/profiles/scheduling)
Every active custom profile stores a UTC cron schedule. Y2 uses it to enqueue the next report run
after the current run reaches a terminal state.
## Scheduling flow
```mermaid
flowchart LR
input["Frequency, day, and UTC time"] --> cron["Stored cron schedule"]
cron --> job["Scheduled report job"]
job --> workflow["Research workflow"]
workflow --> next["Schedule the next run"]
```
## Supported frequencies
| Frequency | Current schedule behavior | Required input |
|---|---|---|
| Daily | Every day at the selected UTC time | Time |
| Weekly | Every selected weekday at the selected UTC time | Weekday and time |
| Biweekly | On the 1st and 15th of each month at the selected UTC time | Time |
| Monthly | On the selected day of the month at the selected UTC time | Day of month and time |
The current Biweekly schedule runs on calendar days 1 and 15. It is not a rolling fourteen-day
interval and does not use the weekly day selector.
A monthly schedule runs only when the selected date exists. For example, a profile scheduled for
the 31st does not run in a thirty-day month.
## Set a schedule
### Open the profile editor
Open [My Profiles](/app/infoops/my-profiles), select the profile, and choose its edit action.
### Open Advanced Configuration
Move to the third editor step. Find **Delivery Schedule** at the top of the page.
### Choose a frequency
Select Daily, Weekly, Biweekly, or Monthly. Weekly adds a weekday selector; Monthly adds a
day-of-month selector.
### Choose the UTC time
Select an hourly time from `00:00` through `23:00`. The editor and backend interpret the value
as UTC.
Convert the desired local time to UTC before saving. Repeat the conversion when your local
timezone enters or leaves daylight saving time.
### Save and verify
Select **Update Feed**, return to **My Profiles**, and confirm that the profile displays the
expected next-run information.
Changing the schedule of an active profile cancels its existing scheduled job, enqueues the new
schedule, and starts a report workflow immediately. Make schedule changes when an extra run is
acceptable.
## Generated cron values
Y2 generates the cron expression; the profile editor does not accept a custom expression.
## Choose an effective cadence
Use for topics where a missed day changes the decision: active incidents, market movement, or
operational disruptions.
Use for recurring reviews where developments should accumulate into a manageable briefing.
Use when twice-monthly calendar reporting is acceptable and strict fourteen-day spacing is not
required.
Use for slower trend reviews, executive summaries, or stable watch areas.
When several profiles serve the same reader, stagger their UTC times. This spreads research work
and prevents multiple reports from arriving together.
## Failure and recovery behavior
Report workflows use bounded action retries and a recovery watchdog for eligible interrupted or
stalled runs. A failed synthesis does not proceed to audio or delivery as if it were complete.
Recovery behavior does not change the profile's stored cadence.
## Next steps
Verify where a completed report is sent by default.
Send completed reports to an HTTPS endpoint.
Return to the full editor workflow and research settings.
---
# Share a Profile (/docs/profiles/sharing)
Turn on **Share with Community** to make a workspace-owned profile discoverable to other Y2 users.
Subscribers follow the same profile; sharing does not create a separate copy for each workspace.
The current new-profile editor starts with **Share with Community** enabled. Turn it off in
Advanced Configuration before selecting **Create Feed** when the profile should remain private.
## How sharing works
```mermaid
flowchart LR
owner["Owning workspace"] --> profile["Shared profile configuration"]
profile --> catalog["Community discovery"]
catalog --> subscriptions["Subscriber workspace records"]
profile --> reports["Future profile reports"]
reports --> subscriptions
```
The owning workspace controls the profile topic, research configuration, cadence, status, and
community visibility. Subscriber workspaces control their own subscription and supported delivery
preferences.
Changes to the shared profile affect later reports for every active subscriber because subscribers
reference the same profile.
## Before sharing
Confirm that the profile:
- Has a clear name and a topic that another reader can understand without private context.
- Uses report sections and instructions appropriate for a broader audience.
- Does not contain secrets, customer data, private names, or confidential domain restrictions.
- Has produced a report you reviewed for scope, source quality, and usefulness.
- Uses a cadence the owning workspace intends to maintain.
Treat the profile name, topic, tags, custom instructions, and research configuration as public
metadata when community sharing is enabled. Do not use a shared profile as a secret store.
## Share an existing profile
### Open the profile editor
Open [My Profiles](/app/infoops/my-profiles), select a profile owned by the active workspace,
and choose its edit action.
### Review the public-facing configuration
Check the name, topic, tags, section titles, and custom instructions. Remove internal-only
details before changing visibility.
### Enable community sharing
Open **Advanced Configuration** and turn on **Share with Community**.
### Save and verify
Select **Update Feed**, then open [Community](/app/infoops/community). Search for the profile
name and confirm that it appears with the expected Global or Community classification.
## What subscribers can do
Subscribers can:
- Discover and open global or community profiles.
- Add a profile to the active workspace when capacity remains.
- Remove their own subscription without changing the shared profile.
- Configure a supported delivery method for their subscription.
They cannot edit or delete the owning workspace's profile.
Subscription limits belong to the active workspace: Free supports one external community or global
subscription, Lite supports three, and Pro or Elite count subscriptions and owned profiles against
their total profile capacity. See [Plans and limits](/docs/plans).
## Stop sharing
### Edit the shared profile
Open the profile from **My Profiles** and move to Advanced Configuration.
### Disable community sharing
Turn off **Share with Community**, then select **Update Feed**.
### Confirm discovery is disabled
Search for the profile in **Community**. It should no longer be available for a new
subscription.
Disabling community sharing removes the profile from public discovery, but it does not
automatically delete subscription records that already exist. Deleting the profile removes its
subscriptions and associated generated resources; treat deletion as a separate destructive
action.
## Maintain a useful shared profile
Make focused refinements. Replacing the subject entirely can surprise existing subscribers.
Add concise domain, geography, industry, and cadence tags that improve discovery.
Check whether sources and conclusions still match the published scope before expanding it.
Y2 does not currently promise an automatic subscriber notification for profile edits. Avoid
silent changes that materially alter the briefing.
## Next steps
Understand the discovery and subscription experience from the reader's perspective.
Refine the topic, sections, source controls, and research behavior.
Choose where completed reports should arrive.
---
# Create Your First Project (/docs/projects/getting-started)
This tutorial creates a Project in the active workspace, attaches one existing resource, and
starts a chat whose context stays inside that Project.
## Before you begin
You need:
- A Lite, Pro, or Elite active workspace.
- An owner, admin, or member role with write access. Elite viewers are read-only.
- At least one report, profile, graph board, or generated image in the same workspace if you want
to attach an existing resource.
## 1. Confirm the active workspace
Use the workspace switcher and select the workspace that should own the Project. Plan access and
resource choices are resolved from this workspace.
## 2. Create the Project
1. Open **Projects** in the app navigation.
2. Select **New Project**.
3. Enter a short name.
4. Add an objective that describes the decision or investigation the Project should support.
5. Optionally add standing instructions for Project-scoped AI work.
6. Save the Project.
The new Project starts active with a private context policy. Its initial workspace view opens the
Graph, Timeline, Situation, Evidence, Knowledge, and Overview tabs.
## 3. Attach an existing resource
Open **Overview**, search the resource picker, and attach a report, profile, graph board, or
generated image. The picker shows only resources owned by you in the active Project workspace.
Attaching a report automatically preserves its stored citations as Project evidence. Attaching a
profile does the same for every prior report in that profile and keeps preserving citations from
future reports. Supported report intelligence can also schedule a graph projection.
## 4. Start a Project chat
Select **New chat** from the Project. Messages written in that thread carry the Project and
workspace identifiers. If you later switch workspaces, Y2 hides the thread until its workspace is
active again.
## 5. Verify the result
Return to **Overview** and confirm that:
- The resource appears under Project resources.
- The thread appears under recent Project chats.
- Timeline activity records the changes.
Create a Project-bound [Automation](/docs/automations/creating) when the objective needs a
scheduled or report-completion follow-up. Lite Projects remain fully usable without Automations.
---
# Projects (/docs/projects)
Projects are private working contexts for a single intelligence objective. A Project connects
related chats, reports, profiles, graph boards, generated images, evidence, and private documents
without mixing them into another workspace or the global knowledge corpus.
Projects use the active workspace's plan. Free workspaces can see the upgrade path but cannot
read or change Project data.
## The Project boundary
Every Project has both a `workspaceId` and an `ownerId`. In this release, that means:
- The Project can use only resources owned by the same user in the same active workspace.
- Project chats and private knowledge stay attached to that Project.
- Switching workspaces removes access until the Project's workspace is active again.
- Other Elite workspace members do not automatically receive access. Project sharing is not yet
supported.
Y2 checks the boundary again for background indexing, graph projection, chat writes, and
Automation runs. A saved identifier alone does not grant access after membership or plan access
changes.
```mermaid
flowchart LR
workspace[Active workspace] --> project[Private Project]
project --> chat[Project chats]
project --> graph[Graph and timeline]
project --> evidence[Evidence archive]
project --> knowledge[Private knowledge]
project --> resources[Reports and profiles]
```
## Project views
| View | Purpose |
|---|---|
| Graph | Maintain a persistent graph for the objective. |
| Timeline | Review attached intelligence and Project activity in time order. |
| Situation | Explore geolocated signals attached to the Project. |
| Evidence | Preserve citations, captures, notes, tags, and lineage. |
| Knowledge | Add private source documents for Project-scoped retrieval. |
| Overview | Review attached resources, recent chats, and eligible Automations. |
Project knowledge is separate from Global Knowledge. Removing a Project binding archives that
binding; it does not publish the content globally.
## Projects and Automations
Projects are available starting with Lite. [Automations](/docs/automations) require Pro or Elite.
A Lite user can still use every Project view, but the Automation card presents the Pro upgrade
path instead of querying protected Automation data.
On Pro or Elite, an Automation can be bound to a Project. Its output thread and run context remain
inside the same owner and workspace boundary.
## Elite governance
Elite workspaces add user-bound audit records for material Project changes, including Project
creation and settings changes, resource and evidence lifecycle actions, graph changes, chat moves,
and tracking-rule changes. Audit records identify the acting user and workspace; they do not make
private Projects visible to other members.
## Choose the next task
Build a Project and add its first intelligence resource.
Attach reports, preserve evidence, add knowledge, and move chats safely.
Review access rules, supported resources, views, limits, and lifecycle behavior.
---
# Organize Intelligence in a Project (/docs/projects/organizing-intelligence)
Use this guide when a Project already exists and you need to keep its supporting material
organized without crossing workspace boundaries.
## Attach reports and profiles
Open **Overview**, use the resource search, choose a role, and attach the item. Supported resource
types are reports, profiles, graph boards, and generated images. Y2 rejects an item that belongs to
another owner or workspace, even if its identifier is supplied directly.
Use roles consistently:
- **Knowledge** for background material.
- **Working** for active analysis.
- **Output** for deliverables.
- **Primary graph** for the main graph board.
## Preserve and classify evidence
Open **Evidence** to preserve citations from linked reports, capture a web source, add notes and
tags, or mark an item important. Evidence lifecycle history records archive, restore, and prune
actions. Trackers can classify future evidence by exact domain or URL prefix.
Pruning is intentionally stricter than archiving. Review downstream lineage before pruning a
source that supports an assertion, report, graph object, or incident.
## Add private knowledge
Open **Knowledge** and upload PDF, text, Markdown, Word, PowerPoint, Excel, OpenDocument, RTF, EPUB,
or CSV files up to 10 MB. PDFs must contain selectable text because scanned pages requiring OCR are
rejected. Uploaded content is indexed only for authorized Project-chat retrieval; it is not added
to the global knowledge corpus. Archive a binding when the source should stop participating in
retrieval, and restore it when needed.
## Maintain the Project graph
Open **Graph** to create the persistent graph, or save an Explorer graph into the Project. Saving
again merges nodes and edges by identifier. A Project graph supports up to 250 nodes and 500
edges.
## Move an existing chat
Move only a chat you own in the active workspace. The target Project must also be active and owned
by you. Once moved, future message access and background Agent Y2 work are checked against the
Project boundary.
## Archive completed work
Archive the Project when the objective is complete. Archiving removes it from active lists and
blocks active-only operations without deleting its history. Restore it from the archived Projects
view when work resumes.
---
# Projects Reference (/docs/projects/reference)
## Availability and access
| Rule | Behavior |
|---|---|
| Minimum plan | Lite |
| Workspace scope | The active workspace must match the Project workspace. |
| Ownership | Owner-private in this release; team sharing is not supported. |
| Write roles | Owner, admin, or member. Elite viewers are read-only. |
| Context policy | `project_only` |
| Elite audit | Material changes include the workspace and acting user. |
## Project fields
| Field | Constraint or meaning |
|---|---|
| Name | Required, up to 80 characters. |
| Objective | Optional, up to 1,000 characters. |
| Instructions | Optional, up to 8,000 characters. |
| Status | `active` or `archived`. |
| Default view | Graph, Timeline, Situation, Reports, Evidence, or Knowledge. |
| Default time window | 24 hours, 7 days, 30 days, or custom. |
| Revision | Used to reject conflicting settings updates. |
## Supported resource types
| Resource | Ownership check |
|---|---|
| Investigation graph board | Same owner and workspace. |
| Profile | Same owner and workspace. |
| Report | Its profile must have the same owner and workspace. |
| Generated image | Same owner and workspace. |
Resource roles are `knowledge`, `working`, `output`, and `primary_graph`.
## View and storage limits
- Persistent graphs: 250 nodes and 500 edges.
- Recent Project chat list: the app displays up to 12 threads.
- Project instructions: 8,000 characters.
- Graph title: 200 characters.
- Private knowledge accepts PDF, text, Markdown, Word, PowerPoint, Excel, OpenDocument, RTF, EPUB,
and CSV files up to 10 MB.
- PDFs are extracted page by page and must contain selectable text; scanned pages requiring OCR are
rejected.
- Private knowledge is capped at 100 documents per owner and workspace, 200 PDF pages, 2,000,000
extracted characters, and 512 indexed chunks per revision.
- Evidence uses a separate archive and integrity contract; evidence attachments are not parsed as
Project knowledge.
## Lifecycle
Archiving is reversible. Project resources, chat association, evidence history, graph state, and
private knowledge remain stored. Active-only mutations reject archived Projects until restored.
Deleting an attached resource link does not delete the original report, profile, graph board, or
image. Evidence pruning and knowledge-source deletion have separate confirmation and retention
behavior in their respective views.
---
# Use Y2 from the Command Line (/docs/sdks/cli)
Use curl for reproducible API checks and jq for JSON selection. These workflows call the same
public contract as an SDK without relying on a separate CLI release or local configuration format.
```mermaid
flowchart LR
A[Environment key] --> B[curl request]
B --> C{Response type}
C -->|JSON| D[jq filter]
C -->|Markdown| E[file]
C -->|NDJSON| F[line processor]
D --> G[shell workflow]
```
## Before you begin
You need curl and, for JSON processing, jq:
```bash
curl --version
jq --version
export Y2_API_KEY="y2_..."
```
New API keys require Pro or Elite. The key must carry the scope required by each operation.
Shell history, debug tracing, process listings, and CI output can expose secrets. Keep the key in
the environment, quote variable expansions, and do not use `set -x` in credentialed scripts.
## Create a reusable GET helper
```bash title="y2.sh"
#!/usr/bin/env bash
set -euo pipefail
: "${Y2_API_KEY:?Set Y2_API_KEY before running this script}"
y2_get() {
local path="$1"
curl --fail-with-body --silent --show-error \
"https://api.y2.dev${path}" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
}
y2_get "/api/v1/news/feeds" | jq '.meta'
```
The helper accepts origin-relative API paths such as `/api/v1/news` and
`/api/v2/incidents`. That also makes it safe to pass a pagination link returned by Y2.
## Discover News topics
List all current topic IDs and names:
```bash
y2_get "/api/v1/news/feeds" |
jq -r '.data[] | [.id, .name, .groupLabel] | @tsv'
```
Then request a bounded page:
```bash
y2_get "/api/v1/news?topics=markets,macro&limit=10" |
jq -r '.data[] | [
.publishedAt,
(.sentiment.label // "unknown"),
.title,
(.links.canonical // "")
] | @tsv'
```
Current News rows use `title`, `summary`, and nested `sentiment` fields. Use the feed directory
instead of assuming an older crypto-only topic list.
## Inspect profile and subscription IDs
`GET /profiles` returns paired resources. Print both IDs before scripting updates:
```bash
y2_get "/api/v1/profiles" |
jq -r '.data[] | [
.profile.id,
.subscription.id,
.profile.name,
.subscription.delivery.method
] | @tsv'
```
Use `prf_...` for profile configuration and report filtering. Use `sub_...` when changing delivery.
## Create a profile idempotently
```bash
profile_id=$(
curl --fail-with-body --silent --show-error \
"https://api.y2.dev/api/v1/profiles" \
--request POST \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: supplier-risk-2026-07-21" \
--data '{
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting strategic semiconductor suppliers.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00"
}' |
jq -r '.data.id'
)
printf '%s\n' "$profile_id"
```
The response ID is the profile's canonical `prf_...` ID. Reuse the same idempotency key only with
the same request body.
## Paginate every report for a profile
Y2 returns an opaque continuation link. Follow it unchanged:
```bash title="export-report-index.sh"
#!/usr/bin/env bash
set -euo pipefail
: "${Y2_API_KEY:?Set Y2_API_KEY}"
: "${PROFILE_ID:?Set PROFILE_ID to a prf_ ID}"
next="/api/v1/reports?profileId=$PROFILE_ID&limit=20"
while [ -n "$next" ]; do
page=$(
curl --fail-with-body --silent --show-error \
"https://api.y2.dev${next}" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
)
jq -r '.data[] | [.id, .publishedAt, (.summary // "")] | @tsv' <<<"$page"
next=$(jq -r '.links.next // empty' <<<"$page")
done
```
Do not decode or edit the cursor. It is bound to the original filters and ordering.
## Export one report as Markdown
```bash
: "${REPORT_ID:?Set REPORT_ID to an rpt_ ID}"
curl --fail-with-body --silent --show-error \
"https://api.y2.dev/api/v1/reports/$REPORT_ID" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: text/markdown" \
--output "$REPORT_ID.md"
```
The file contains the report body directly. It is not a JSON envelope.
## Process NDJSON one row at a time
News and several list endpoints can return newline-delimited JSON:
```bash
curl --fail-with-body --silent --show-error --get \
"https://api.y2.dev/api/v1/news" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/x-ndjson" \
--data-urlencode "topics=cybersecurity" \
--data-urlencode "limit=50" |
while IFS= read -r row; do
jq -r '[.publishedAt, .title] | @tsv' <<<"$row"
done
```
For multi-page ingestion, also capture `X-Y2-Next-Cursor` from the response headers and send it as
the next request's `cursor` value.
## Capture status, headers, and errors
Use temporary files when a script needs to branch on HTTP status while retaining Problem Details:
```bash
body_file=$(mktemp)
headers_file=$(mktemp)
trap 'rm -f "$body_file" "$headers_file"' EXIT
status=$(
curl --silent --show-error \
--output "$body_file" \
--dump-header "$headers_file" \
--write-out '%{http_code}' \
"https://api.y2.dev/api/v1/reports/not-a-report-id" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
)
if [ "$status" -ge 400 ]; then
jq '{status, code, detail, requestId}' "$body_file" >&2
grep -i '^Retry-After:' "$headers_file" >&2 || true
exit 1
fi
```
Log `requestId`, not credentials. Retry safe reads or idempotent writes only, and follow
`Retry-After` on `429`.
## Call Intel v2
Intel paths use `/api/v2` directly:
```bash
y2_get "/api/v2/incidents?limit=20" |
jq -r '.data[] | [.id, .severity, .title] | @tsv'
```
Do not place `/api/v1` before an Intel v2 path.
## Know when curl is not enough
curl can inspect the initial `402` and `PAYMENT-REQUIRED` header, but it does not create an
EIP-3009 signature by itself. Use an x402 v2-capable wallet client, then retry with the generated
`PAYMENT-SIGNATURE`.
Agent Y2 uses SSE in the Vercel AI SDK data-stream format. curl can display raw frames, but an
application should use a parser that understands the stream and retains `X-Thread-Id`.
curl is useful for management endpoints, but testing delivery requires a public HTTPS receiver
that preserves raw request bytes for signature verification.
---
# Client Libraries and Tools (/docs/sdks)
Y2 publishes an OpenAPI 3.1 contract that can drive types, generated clients, request tools, and
tests. Treat that contract—not a package's convenience method names—as the compatibility boundary.
```mermaid
flowchart LR
A[OpenAPI 3.1 contract] --> B[Generated types]
A --> C[Generated client]
A --> D[API Workbench]
A --> E[Endpoint reference]
B --> F[Your transport wrapper]
C --> G[Your application]
F --> G
```
New API-key access requires Pro or Elite. Existing grandfathered Lite integrations retain their
existing keys during the grace period. Create a key in the active workspace and grant only the
scopes the client needs.
## Choose an integration style
Generate path and schema types, then call Y2 with a small typed `fetch` wrapper.
Build a synchronous or asynchronous HTTP client around the published response contract.
Use curl and jq for smoke tests, exports, and automation without an SDK dependency.
Explore path and query parameters, run live reads, and copy starter snippets.
| Option | Best for | Contract fidelity | Main responsibility |
| --- | --- | --- | --- |
| Generated OpenAPI client | Broad API coverage | Regenerate when the spec changes | Review generator behavior |
| Generated types + native HTTP | Controlled production integrations | Types come directly from OpenAPI | Maintain a small transport layer |
| curl or Workbench | Exploration and operations | Request is visible and explicit | Handle parsing and pagination |
| Separately released package | Convenience APIs | Depends on its release date | Verify version and operation coverage |
Package installation names, exported classes, and helper methods are release-specific and are not
defined by the platform OpenAPI file. Before adopting a separately released client, compare its
generated operation IDs and models with the current endpoint reference.
## Start from the contract
Download the production description:
```bash
curl --fail --silent --show-error \
"https://y2.dev/api/openapi.yaml" \
--output openapi.yaml
```
Then validate the assumptions that most often break generated clients:
- v1 paths inherit `https://api.y2.dev/api/v1`;
- Intel v2 operations override the server with `https://api.y2.dev`;
- bearer scopes are declared in `x-required-scopes`;
- supported reads can declare x402 as a second security mode;
- errors use `application/problem+json` with stable top-level `code` and `requestId`;
- list pagination uses `links.next` and `meta.page.nextCursor` where applicable; and
- some endpoints can return NDJSON, GeoJSON, Markdown, plain text, audio redirects, or SSE.
## Use a minimal bearer request
Every bearer client needs the same basic transport behavior:
```bash
export Y2_API_KEY="y2_..."
curl --fail-with-body \
"https://api.y2.dev/api/v1/news?topics=markets&limit=5" \
--header "Authorization: Bearer $Y2_API_KEY" \
--header "Accept: application/json"
```
Keep the API key on a server, worker, job runner, or local development machine. Do not ship it in a
browser bundle or mobile application.
## Model the common response shapes
```mermaid
flowchart TD
A[HTTP response] --> B{Status 2xx?}
B -->|No| C[Problem Details]
B -->|Yes| D{Representation}
D -->|Collection| E[data array + meta + links]
D -->|Single JSON| F[data object + optional meta]
D -->|Streaming or file| G[Endpoint-specific transport]
```
Do not assume every successful response is `{ data: [...] }`. A generated client must preserve
declared media types and status codes, especially `204` deletes, `302` audio redirects,
`text/event-stream`, `application/x-ndjson`, `application/geo+json`, and `text/markdown`.
## Regenerate safely
### Pin the input
Generate from a reviewed copy of `https://y2.dev/api/openapi.yaml` or pin the generated output
in source control. Avoid silently regenerating from a moving URL during every production build.
### Review operation IDs and servers
Stable operation IDs become method names in many generators. Confirm that v2 paths do not
receive the v1 prefix.
### Add transport policies
Configure timeouts, bounded retries, rate-limit handling, idempotency keys for supported writes,
and secret management outside generated files.
### Smoke-test one operation per representation
Test ordinary JSON, pagination, a mutation, and any special representation your integration
consumes. Generated types alone do not prove deployed runtime behavior.
## Coverage checklist
Use the generated reference to verify the exact operation rather than inferring coverage from a
client namespace.
| Surface | Special handling to verify |
| --- | --- |
| Profiles | Separate `prf_` profile and `sub_` subscription resources; PUT versus PATCH |
| Reports | Compact defaults, bounded includes, Markdown/text/audio representations |
| News | Topic catalog, JSON or NDJSON pagination, cache-readiness errors |
| OSINT | JSON, NDJSON, and GeoJSON; source availability varies |
| Intel v2 | Operation-level server override and ontology IDs |
| Webhooks | Idempotency, ETags, write-only secrets, and `204` deletion |
| Agent Y2 | API-key-only SSE transport, not an ordinary JSON response |
| x402 | Payment challenge and settlement headers outside normal bearer auth |
---
# Build a Python Client (/docs/sdks/python)
This guide builds a dependency-free Python client around Y2's public HTTP contract. Use it directly
for small services and jobs, or use the same behaviors as acceptance criteria for a generated client.
```mermaid
flowchart LR
A[Y2_API_KEY] --> B[Python transport]
B --> C[JSON envelopes]
B --> D[Problem Details]
C --> E[Domain workflow]
D --> F[Retry or fail]
```
Load `Y2_API_KEY` from the environment or a secret manager. Notebook output, exception dumps,
and committed `.env` files can leak credentials as easily as application logs.
## 1. Configure the environment
The examples use Python 3.11 or newer and only the standard library:
```bash
export Y2_API_KEY="y2_..."
python --version
```
New keys require a Pro or Elite workspace and the scopes needed by the operations you call.
## 2. Create the transport
```python title="y2_client.py"
from __future__ import annotations
import json
import os
from dataclasses import dataclass
from typing import Any, Mapping, TypeVar, cast
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import Request, urlopen
T = TypeVar("T")
API_ORIGIN = "https://api.y2.dev"
API_V1 = f"{API_ORIGIN}/api/v1"
class Y2ApiError(Exception):
def __init__(
self,
status: int,
problem: dict[str, Any] | None,
retry_after: str | None,
request_id: str | None,
) -> None:
detail = problem.get("detail") if problem else None
super().__init__(detail or f"Y2 request failed with HTTP {status}")
self.status = status
self.problem = problem
self.retry_after = retry_after
self.request_id = request_id
@dataclass(frozen=True)
class Y2Client:
api_key: str
timeout_seconds: float = 30.0
@classmethod
def from_env(cls) -> Y2Client:
api_key = os.environ.get("Y2_API_KEY")
if not api_key:
raise RuntimeError("Y2_API_KEY is required")
return cls(api_key=api_key)
def request_json(
self,
method: str,
path_or_url: str,
*,
query: Mapping[str, str | int | float | bool | None] | None = None,
body: Mapping[str, Any] | None = None,
headers: Mapping[str, str] | None = None,
) -> T:
if path_or_url.startswith("https://"):
url = path_or_url
elif path_or_url.startswith("/api/"):
url = f"{API_ORIGIN}{path_or_url}"
else:
url = f"{API_V1}{path_or_url}"
params = {
key: str(value).lower() if isinstance(value, bool) else value
for key, value in (query or {}).items()
if value is not None
}
if params:
separator = "&" if "?" in url else "?"
url = f"{url}{separator}{urlencode(params)}"
request_headers = {
"Authorization": f"Bearer {self.api_key}",
"Accept": "application/json",
**(headers or {}),
}
data = None
if body is not None:
request_headers.setdefault("Content-Type", "application/json")
data = json.dumps(body, separators=(",", ":")).encode("utf-8")
request = Request(url, data=data, headers=request_headers, method=method)
try:
with urlopen(request, timeout=self.timeout_seconds) as response:
raw = response.read()
if response.status == 204 or not raw:
return cast(T, None)
return cast(T, json.loads(raw))
except HTTPError as error:
raw = error.read()
problem = None
if raw:
try:
decoded = json.loads(raw)
if isinstance(decoded, dict):
problem = decoded
except json.JSONDecodeError:
pass
raise Y2ApiError(
error.code,
problem,
error.headers.get("Retry-After"),
error.headers.get("X-Request-Id"),
) from error
```
This helper handles JSON and `204 No Content`. Create separate response readers for Markdown, plain
text, NDJSON, GeoJSON, audio redirects, and SSE instead of forcing every media type through
`json.loads`.
## 3. Read current News items
```python title="news.py"
from typing import Any, TypedDict
from y2_client import Y2Client
class NewsPage(TypedDict):
data: list[dict[str, Any]]
meta: dict[str, Any]
links: dict[str, str | None]
client = Y2Client.from_env()
page: NewsPage = client.request_json(
"GET",
"/news",
query={"topics": "markets,macro", "limit": 10},
)
for item in page["data"]:
print(item["title"], item["url"])
```
Use `GET /news/feeds` to discover the current 40-topic catalog. Current items use `title`, `summary`,
and a nested `sentiment.label`/`sentiment.value` object—not older top-level `signal` and sentiment
scalar fields.
## 4. Create a profile safely
```python title="profiles.py"
from typing import Any
from y2_client import Y2Client
client = Y2Client.from_env()
created: dict[str, Any] = client.request_json(
"POST",
"/profiles",
headers={"Idempotency-Key": "supplier-risk-2026-07-21"},
body={
"name": "Critical Supplier Risk",
"topic": "Monitor disruption affecting strategic semiconductor suppliers.",
"frequency": "daily",
"scheduleTimeOfDay": "08:00",
},
)
profile_id = created["data"]["id"]
print(profile_id) # prf_...
```
Creation returns the profile in `data`. It also creates an active subscription, but retrieve that
separate `sub_...` resource from `GET /profiles` when you need to change delivery.
Use `PATCH /profiles/{profileId}` for partial changes. `PUT` replaces the complete mutable state,
and omitted optional values reset or clear. Retain returned `ETag` headers if your workflow uses
`If-Match`; a JSON-only wrapper that discards headers should be extended for that workflow.
## 5. Follow report pagination
```python title="reports.py"
from collections.abc import Iterator
from typing import Any
from y2_client import Y2Client
def reports_for_profile(
client: Y2Client,
profile_id: str,
) -> Iterator[dict[str, Any]]:
next_url: str | None = "/reports"
first = True
while next_url:
page: dict[str, Any] = client.request_json(
"GET",
next_url,
query={"profileId": profile_id, "limit": 20} if first else None,
)
yield from page["data"]
next_url = page["links"]["next"]
first = False
client = Y2Client.from_env()
for report in reports_for_profile(client, "prf_0123456789abcdef01234567"):
print(report["id"], report["summary"])
```
Treat `links.next` and its cursor as opaque. Do not decode, edit, or combine a continuation link
with different filters.
## 6. Run independent reads concurrently
The standard-library transport is synchronous. For a small job, run independent calls in worker
threads without sharing mutable client state:
```python title="concurrent_reads.py"
import asyncio
from y2_client import Y2Client
async def main() -> None:
client = Y2Client.from_env()
news, feeds = await asyncio.gather(
asyncio.to_thread(
client.request_json,
"GET",
"/news?topics=markets&limit=10",
),
asyncio.to_thread(client.request_json, "GET", "/news/feeds"),
)
print(len(news["data"]), len(feeds["data"]))
asyncio.run(main())
```
For a high-throughput service, replace the transport with an async HTTP client that supports
connection pooling, but keep the same authentication, timeout, error, pagination, and media-type
rules.
## Handle Problem Details
```python title="errors.py"
from y2_client import Y2ApiError, Y2Client
client = Y2Client.from_env()
try:
client.request_json("GET", "/reports/not-a-report-id")
except Y2ApiError as error:
code = error.problem.get("code") if error.problem else "UNKNOWN"
print(error.status, code, error.request_id)
if error.status == 429:
print("Retry after", error.retry_after)
```
Retry only safe reads or writes protected by an idempotency key. A `404` on tenant-owned resources
can intentionally avoid revealing whether another tenant owns the ID.
## Call Intel v2 with the correct base
Intel path keys already contain `/api/v2`, and their OpenAPI operation overrides the top-level v1
server:
```python
incidents = client.request_json(
"GET",
"https://api.y2.dev/api/v2/incidents",
query={"limit": 20},
)
```
Do not prepend `https://api.y2.dev/api/v1` to an Intel v2 path.
## Evaluate a generated Python client
If you generate models and operation methods from OpenAPI, verify that the result:
- honors operation-level server overrides;
- represents nullable and optional fields separately;
- exposes `application/problem+json` rather than replacing it with generic exceptions;
- preserves response headers for rate limits, ETags, request IDs, and x402;
- handles `204` and `302` without trying to decode JSON; and
- provides explicit streaming APIs for NDJSON and Agent Y2 SSE.
A generated package can reduce boilerplate, but its published method names and release cadence are
separate from the Y2 API contract. Compare its generated operation IDs with the current endpoint
reference before adopting it.
---
# Build a TypeScript Client (/docs/sdks/typescript)
Use generated OpenAPI types with native `fetch` when you want current schema coverage without
depending on release-specific SDK method names. This approach works in Node.js, Bun, serverless
functions, and workers with standards-compatible fetch APIs.
```mermaid
flowchart LR
A[openapi.yaml] --> B[y2-api.d.ts]
B --> C[Typed request and response aliases]
C --> D[Small fetch transport]
D --> E[Application service]
```
Do not instantiate a bearer client in browser-delivered code. Put Y2 calls behind your own
server, worker, or API route so `Y2_API_KEY` never enters a public bundle.
## 1. Generate declarations
Install the same generator used by the Y2 platform repository:
```bash
bun add --dev openapi-typescript@^7.13.0
bunx openapi-typescript https://y2.dev/api/openapi.yaml \
--immutable \
--alphabetize \
--output src/y2-api.d.ts
```
```bash
npm install --save-dev openapi-typescript@^7.13.0
npx openapi-typescript https://y2.dev/api/openapi.yaml \
--immutable \
--alphabetize \
--output src/y2-api.d.ts
```
Commit the generated declaration or pin the OpenAPI input in your build pipeline. Regeneration from
a moving production URL should be an intentional compatibility update, not an invisible deploy step.
Add a repeatable script:
```json title="package.json"
{
"scripts": {
"generate:y2-types": "openapi-typescript https://y2.dev/api/openapi.yaml --immutable --alphabetize --output src/y2-api.d.ts"
}
}
```
## 2. Create a JSON transport
Set the credential in the runtime environment:
```bash
export Y2_API_KEY="y2_..."
```
Then create a transport that preserves Y2 Problem Details and rate-limit context:
```typescript title="src/y2-client.ts"
import type { components } from "./y2-api";
const API_ORIGIN = "https://api.y2.dev";
const API_V1 = `${API_ORIGIN}/api/v1`;
export type Y2Problem = components["schemas"]["ErrorResponse"];
export class Y2ApiError extends Error {
constructor(
readonly status: number,
readonly problem: Y2Problem | null,
readonly retryAfter: string | null,
readonly requestId: string | null,
) {
super(problem?.detail ?? `Y2 request failed with HTTP ${status}`);
}
}
function isProblem(value: unknown): value is Y2Problem {
return (
typeof value === "object" &&
value !== null &&
"status" in value &&
"code" in value &&
"detail" in value
);
}
function resolveUrl(pathOrUrl: string): string {
if (pathOrUrl.startsWith("https://")) return pathOrUrl;
if (pathOrUrl.startsWith("/api/")) return `${API_ORIGIN}${pathOrUrl}`;
return `${API_V1}${pathOrUrl}`;
}
export async function y2Json(
pathOrUrl: string,
init: RequestInit = {},
): Promise {
const apiKey = process.env.Y2_API_KEY;
if (!apiKey) throw new Error("Y2_API_KEY is required");
const headers = new Headers(init.headers);
headers.set("Authorization", `Bearer ${apiKey}`);
if (!headers.has("Accept")) headers.set("Accept", "application/json");
if (init.body && !headers.has("Content-Type")) {
headers.set("Content-Type", "application/json");
}
const response = await fetch(resolveUrl(pathOrUrl), {
...init,
headers,
signal: init.signal ?? AbortSignal.timeout(30_000),
});
const text = await response.text();
let payload: unknown = null;
if (text) {
try {
payload = JSON.parse(text);
} catch {
payload = text;
}
}
if (!response.ok) {
throw new Y2ApiError(
response.status,
isProblem(payload) ? payload : null,
response.headers.get("Retry-After"),
response.headers.get("X-Request-Id"),
);
}
return payload as T;
}
```
`y2Json` is intentionally JSON-only. Use separate functions for NDJSON, Markdown, plain text, audio
redirects, and Agent Y2 SSE so a content-type mismatch cannot be silently cast to a JSON model.
## 3. Type a read operation
Extract parameter and response types from the path entry:
```typescript title="src/news.ts"
import type { paths } from "./y2-api";
import { y2Json } from "./y2-client";
type ListNewsQuery = NonNullable<
paths["/news"]["get"]["parameters"]["query"]
>;
type ListNewsResponse =
paths["/news"]["get"]["responses"][200]["content"]["application/json"];
export async function listNews(query: ListNewsQuery): Promise {
const params = new URLSearchParams();
if (query.topics) params.set("topics", query.topics);
if (query.limit !== undefined) params.set("limit", String(query.limit));
if (query.cursor) params.set("cursor", query.cursor);
if (query.format) params.set("format", query.format);
return y2Json(`/news?${params}`);
}
const page = await listNews({ topics: "markets,macro", limit: 10 });
for (const item of page.data) {
console.log(item.title, item.url);
}
```
Use `GET /news/feeds` to discover the current 40-topic catalog. Current items use `title`, `summary`,
and a nested `sentiment.label`/`sentiment.value` object—not older top-level `signal` and sentiment
scalar fields.
## 4. Type a mutation
Profile creation returns a `PublicProfile` directly in `data`. Its ID is `data.id`; profile creation
also creates a subscription, but that `sub_...` ID is obtained from `GET /profiles`.
```typescript title="src/profiles.ts"
import type { paths } from "./y2-api";
import { y2Json } from "./y2-client";
type CreateProfileBody =
paths["/profiles"]["post"]["requestBody"]["content"]["application/json"];
type CreateProfileResponse =
paths["/profiles"]["post"]["responses"][201]["content"]["application/json"];
export async function createProfile(
body: CreateProfileBody,
idempotencyKey: string,
): Promise {
return y2Json("/profiles", {
method: "POST",
headers: { "Idempotency-Key": idempotencyKey },
body: JSON.stringify(body),
});
}
const created = await createProfile(
{
name: "Critical Supplier Risk",
topic: "Monitor disruption affecting strategic semiconductor suppliers.",
frequency: "daily",
scheduleTimeOfDay: "08:00",
},
"supplier-risk-2026-07-21",
);
console.log(created.data.id); // prf_...
```
Use `PATCH /profiles/{profileId}` for partial changes and `PUT` only for full replacement. Preserve
the `ETag` returned by create or update when you need `If-Match` concurrency protection.
## 5. Paginate reports
Follow the response link instead of decoding or modifying an opaque cursor:
```typescript title="src/reports.ts"
import type { paths } from "./y2-api";
import { y2Json } from "./y2-client";
type ReportsPage =
paths["/reports"]["get"]["responses"][200]["content"]["application/json"];
export async function* reportsForProfile(profileId: string) {
let next: string | null = `/reports?profileId=${encodeURIComponent(profileId)}&limit=20`;
while (next) {
const page: ReportsPage = await y2Json(next);
yield* page.data;
next = page.links.next;
}
}
for await (const report of reportsForProfile("prf_0123456789abcdef01234567")) {
console.log(report.id, report.summary);
}
```
The list and default detail response are compact. Request `include=content,sources` or
`Accept: text/markdown` only when the consumer needs those representations.
## Call Intel v2 correctly
The OpenAPI path key includes the full `/api/v2` prefix, and the operation overrides the document
server with `https://api.y2.dev`:
```typescript
type IncidentList =
paths["/api/v2/incidents"]["get"]["responses"][200]["content"]["application/json"];
const incidents = await y2Json(
"https://api.y2.dev/api/v2/incidents?limit=20",
);
```
Do not compose Intel URLs by adding `/api/v1` to the path.
## Handle special representations
Request `application/x-ndjson`, read `response.body` as a stream, split complete newline-delimited
records, and retain `X-Y2-Next-Cursor`. Do not call `response.json()` on the whole stream.
Send `Accept: text/markdown` to `GET /reports/{reportId}` and use `response.text()`. The response
is the report body, not a `{ data }` envelope.
Agent Y2 returns `text/event-stream` in the Vercel AI SDK data-stream format. Use an SSE-aware
streaming consumer and retain `X-Thread-Id`; do not route it through `y2Json`.
## Production checklist
- Pin or review generated declaration changes.
- Give the key only the required scopes.
- Use idempotency keys on supported create operations.
- Follow `Retry-After` on `429` and retry only safe or idempotent requests.
- Log `X-Request-Id` without logging credentials or full payment payloads.
- Treat `404` as non-disclosing for tenant-owned resources.
- Test special media types separately from JSON.
---
# Vision (/docs/vision)
Y2's vision is an intelligence workspace where every decision-maker can maintain relevant context,
understand what changed, and act without rebuilding the research process from scratch.
This page explains the design direction of Y2. For current product availability, use
[Plans and limits](/docs/plans) and the [Changelog](/docs/changelog).
## The intelligence loop
```mermaid
flowchart LR
sources["Open sources and operator inputs"] --> context["Connected intelligence context"]
context --> research["Human and agent research"]
research --> outputs["Briefings, alerts, graphs, and APIs"]
outputs --> decisions["Operator decisions"]
decisions -. "new priorities" .-> context
```
The goal is not a larger stream of information. It is a durable loop in which new evidence updates
shared context, research produces an inspectable result, and operator priorities shape the next
cycle.
## Design direction
### Persistent rather than one-off
A question that matters today will often matter again tomorrow. Profiles and saved workspace
context should make recurring monitoring easier than restarting the same search.
### Connected rather than isolated
Reports, events, entities, sources, and investigations should reinforce one another. Readers should
be able to move from a summary to the evidence, relationships, and prior context behind it.
### Human-directed agents
AI should extend an analyst's reach while preserving user control. The operator defines the goal,
reviews the evidence, and decides what action—if any—to take.
### Usable wherever decisions happen
Intelligence should work in the Y2 interface and through delivery channels, APIs, and interoperable
contracts. A useful result should not be trapped in one screen.
### Compounding community knowledge
Reusable profiles and shared intelligence patterns can reduce duplicated work. Community knowledge
becomes more valuable when its authorship, scope, and source context remain understandable.
## What remains constant
- Operators control the topics, workspace, and delivery paths that matter to them.
- Source context and limitations remain visible enough to support independent judgment.
- Product capabilities are documented as they exist, separately from long-term direction.
- Integrations use explicit contracts so teams can build without depending on hidden behavior.
## Continue exploring
Understand the access and decision-support problem Y2 is designed to address.
Compare available workspace features and enforced limits.
Follow shipped improvements and current release notes.
---
# InfoOps Report Workflow (/docs/workflows)
Every active InfoOps profile runs through a fixed, durable report workflow. The workflow searches
the web, optionally expands the topic into child searches, synthesizes one report, enriches it,
delivers it to subscribers, and schedules the next run.
This page documents the built-in report pipeline configured through an InfoOps profile. Pro and
Elite users can also create [Automations](/docs/automations) that run bounded Agent Y2 work on a
schedule or after a profile report completes.
```mermaid
flowchart LR
A[Profile trigger] --> B[Root web research]
B --> C{Recursive layer?}
C -->|Enabled| D[2-3 parallel child searches]
C -->|Disabled| E[Final synthesis]
D --> E
E --> F[Optional audio]
F --> G[Required enrichment]
G --> H[Subscriber delivery]
H --> I[Update state and schedule next run]
```
## How a run starts
A report run is attached to a profile, not created as an independent workflow object. Y2 queues a
run when:
- An active custom profile is created.
- A paused or cancelled profile becomes active.
- An active profile's status or schedule changes.
- Its next scheduled execution time arrives.
- Recovery logic restarts a failed or stale run.
The start guard skips inactive profiles, prevents a second live run for the same profile, and
defers new starts while a retry is already scheduled. Creating an active profile therefore starts
an initial report immediately and also schedules its next normal occurrence.
The workflow engine runs for active profiles on every plan. Plan-specific profile, delivery,
audio, API, and workspace limits still apply to the surrounding features.
## Research and synthesis
### 1. Search the root topic
Y2 builds a search query from the profile topic and topic-specific hints, then calls Tavily. The
research action honors these saved search fields:
The search action validates freshness using the engine's default topic-aware rules. When it finds
stale sources and still has more than three results, it removes stale URLs before synthesis.
The profile's **Source Freshness** and **Link Validation** values are stored, but the current
research action uses its centralized default freshness configuration rather than those saved
switches. Domain and time-range controls above are applied directly.
### 2. Expand one child layer
When recursion is enabled, an AI step proposes two or three concise, search-ready subtopics. Y2
researches those child topics in parallel and merges successful findings and unique source URLs
with the root results. A failed child does not discard successful root or sibling research.
The engine is breadth-first and caps effective recursion at one child layer. It uses a 480-second
overall research budget and reserves up to 120 seconds for the child batch before final synthesis.
The current profile editor offers 1-, 2-, and 3-layer labels, but it always saves recursion as
enabled. At runtime, an enabled depth of `0` defaults to `1`, and values above `1` are capped at
`1`. The three app choices therefore execute at most one child layer. A public API profile with
`recursionConfig.enabled: false` is the current way to request a true root-only run.
The saved `strategy` value is accepted by profile schemas, but the current engine always runs the
implemented breadth-first path.
### 3. Synthesize one report
Only the root layer performs synthesis. Y2 combines root and child findings, deduplicates sources,
and truncates the combined research document at 35,000 characters before generating one report.
The synthesis prompt includes:
- The profile topic and up to 2,000 characters of custom instructions.
- The configured report-section titles, or the default BLUF structure.
- Resolved branding terminology.
- A compact summary of up to three previous reports for continuity.
- A structured SIGINT contract and the discovered source URLs.
Y2 rejects incomplete synthesis that lacks required sections, ends abruptly, or contains no
usable signals. A failed quality check stops the workflow before audio or delivery rather than
sending a partial report.
## Models and provider routing
The app does not expose a report-model selector. The current subtopic and synthesis cascades are
fixed in the workflow code:
| Stage | Primary | Fallback order |
| --- | --- | --- |
| Subtopic identification | `z-ai/glm-5.2` | `google/gemini-3.1-flash-lite` |
| Final synthesis | `z-ai/glm-5.2` | `minimax/minimax-m2.7`, then `google/gemini-3.1-flash-lite` |
On a network failure, a model can be retried once. Parse, timeout, context, or structured-output
failures can advance to the next distinct model. If subtopic generation exhausts retryable model
attempts, Y2 can use three deterministic child-query templates; final synthesis has no equivalent
partial-report fallback.
Public profile schemas currently accept `modelConfig.modelId`, but the executed subtopic and
synthesis model order comes from the fixed cascades above. Temperature and maximum output-token
overrides are read; a custom model ID does not replace the cascade's primary model.
Provider requests deny data collection through the configured OpenRouter routing options. See the
platform's privacy and data-processing terms for the complete data-handling boundary.
## Post-processing and delivery
After synthesis, the durable workflow runs these steps in order:
### Generate optional audio
If the profile has Audio Narration enabled, Y2 attempts to create and store the MP3. Audio is
non-critical; a failed audio step does not block the report.
### Enrich the report
Y2 extracts report events and builds the ontology graph and incident metadata used by
downstream product surfaces. Enrichment is required and must finish before delivery.
### Deliver to subscribers
Y2 fans the report out according to each active subscription's email, SMS, combined, or
webhook configuration. Delivery is critical; a terminal delivery failure fails the run.
### Finalize and reschedule
Y2 updates the profile's last-delivery timestamp and hands off the next normal schedule.
Recovery logic repairs the schedule when that final handoff fails.
The compact webhook event contains report state, signal and graph counts, audio availability, and
links to richer API representations. It does not embed the full report body. See
[Webhook Delivery](/docs/delivery/webhooks) for the current contract.
## Failure recovery
Y2 records report-generation state on the profile as `generating`, `completed`, `failed`, or
`canceled`. A run receives a 45-minute lease. The recovery watchdog can renew an in-progress lease
once for 30 minutes before treating an unresolved run as stale.
Terminal failures can be retried up to three times with delays of 5 minutes, 15 minutes, and 1
hour. When workflow history is available, recovery restarts from the last failed durable step;
otherwise it queues a fresh run. After retries are exhausted, Y2 returns to repairing the normal
profile schedule instead of retrying indefinitely.
Duplicate starts are skipped while a valid generation lease or future retry exists. If a newly
activated profile does not immediately produce a second report, an existing run may already be
active or waiting for recovery.
## Configure the implemented workflow
### Define the research target
In **InfoOps → My Profiles**, create or edit a profile. Use a specific topic and custom
instructions that describe the desired decisions, geography, entities, and output emphasis.
### Choose report structure and depth
Set the BLUF sections and review the recursion limitation above. Use the public Profiles API
with recursion disabled when a root-only run is required.
### Set search boundaries
In **Advanced Configuration**, choose search depth, time range, result count, and optional
include or exclude domains.
### Configure delivery and optional outputs
Set the schedule and subscriber method, then enable audio or branding only when the active
workspace has those entitlements.
### Activate and review the first report
Saving a new active profile queues an immediate run. Review its sources, signals, graph, and
delivery history before relying on later scheduled reports.
## API boundary
There is no public endpoint for creating arbitrary workflows or directly starting an internal
workflow ID. Use the Profiles API to create, update, activate, pause, or cancel the configuration;
use the Reports API to read produced reports and derived representations.
The generated [Profiles reference](/docs/api/reference/profiles) is the canonical request schema.
Profile writes require `profiles:write`; report reads require the relevant `reports:*` scope.
## Next steps
Configure the inputs that feed the durable report pipeline.
Understand UTC scheduling, jitter, and schedule recovery.
Configure the optional narration step.
Consume the compact report-generated event safely.
---
# Workspaces (/docs/workspaces)
A workspace is Y2's tenant boundary for plan entitlements, limits, resources, API keys, usage, and
team membership. Every account has a personal workspace. You can also own one organization
workspace and join organization workspaces owned by other people.
Workspace type and plan are separate:
- a **personal** workspace can hold a Free, Lite, Pro, or Elite plan;
- a newly created **organization** workspace starts on Free;
- member seats, team governance, and workspace audit logs activate only when the organization is
on Elite.
## How workspace scope works
```mermaid
flowchart LR
user["Signed-in user"]
switcher["Active workspace"]
membership["Membership check
role · seat status"]
plan["Workspace plan
features · limits · budgets"]
resources["Tenant resources
profiles · reports · chat · API keys · usage"]
user --> switcher --> membership
membership --> plan
membership --> resources
plan --> resources
```
The active workspace is stored on your user record. When you switch, Y2 resolves the new
membership and uses that workspace for tenant-scoped reads and writes. A suspended membership is
not a valid active scope.
The workspace boundary covers resources such as profiles, reports, subscriptions, delivery logs,
webhooks, branding and pronunciation settings, chat and generated images, API keys and their usage,
investigations, OSINT watchlists, alerts, and snapshots.
Changing the active workspace changes which tenant Y2 reads and writes. It does not transfer
existing profiles, reports, API keys, or other resources between workspaces.
## Switch workspaces
### Open the workspace menu
In the app sidebar, select the workspace name above the main navigation.
### Review the context
Each entry shows its name, type, and plan. Your role is also visible under **Settings →
Workspace**.
### Select a workspace
Choose another active membership. The app updates the active tenant context immediately.
### Confirm before making changes
Check the workspace name again before creating a profile, API key, webhook, or investigation.
The new resource is assigned to the active workspace.
## Create an organization workspace
Open the workspace menu or **Settings → Workspace**, then select **New organization**. Enter a name
from 2 to 80 characters.
The organization becomes active after creation and starts on Free. Your personal workspace remains
separate. To enable team seats, either upgrade the organization to Elite or move an existing paid
personal plan to it and ensure that plan is Elite.
The current creation path reuses the organization workspace you already own instead of creating a
second owned organization. You can still be a member of additional organizations owned by other
users.
## Move a paid plan to an organization
If you own a paid personal workspace and a Free organization workspace, **Settings → Workspace**
shows **Move workspace plan**.
### Select the organization target
You must be the owner of both the paid personal workspace and the target organization.
### Move the plan
Y2 attaches the active paid subscription and its plan entitlements to the organization, copies
the Stripe customer reference when needed, and makes the organization active.
### Review both workspaces
The source personal workspace is demoted to Free. Its existing tenant resources stay there;
only the billing plan moves.
Moving a Lite or Pro plan does not unlock team seats because organization membership management is
an Elite feature.
## Roles and enforced management permissions
| Operation | Owner | Admin | Member | Viewer |
| --- | :---: | :---: | :---: | :---: |
| Switch into an active seat | Yes | Yes | Yes | Yes |
| View the member roster | Yes | Yes | Yes | Yes |
| Rename the workspace | Yes | Yes | No | No |
| Invite people and revoke invites | Yes | Yes | No | No |
| View governance and audit activity | Yes | Yes | No | No |
| Create and manage workspace API keys | Yes | Yes | No | No |
| Change another member's role | Yes | No | No | No |
| Remove another member | Yes | No | No | No |
| Manage billing or move the paid plan | Yes | No | No | No |
Owners cannot be removed or assigned another role. A non-owner can remove their own membership.
Organization deletion is currently available to both owners and admins when every deletion blocker
has been cleared.
Y2 checks role requirements at each resource operation. For example, owners and admins can manage
all workspace profiles; members can write profiles they created; viewers do not have profile write
access. Team investigations also distinguish read membership from owner/member write access.
Review the relevant feature guide instead of treating the role name alone as a guarantee for
every object type.
## Invite teammates on Elite
Only an Elite organization exposes active team management.
### Open Members
Switch to the organization, then go to **Settings → Members**. Owners and admins can create
invitations.
### Choose the email and role
Select `admin`, `member`, or `viewer`. The owner role cannot be invited. The owner plus active
members and unexpired open invitations must fit within the five-seat cap.
### Send the invitation
Y2 creates a unique token, emails the invite, and keeps it valid for seven days. Inviting the
same email while an open invite exists resends the existing invite.
### Accept with the matching account
The signed-in account's normalized email must match the invited email. Acceptance preserves or
creates a separate personal workspace, adds the organization membership, and switches the
invitee into the organization.
Owners can later change non-owner roles or remove members. Owners and admins can revoke pending
invitations.
## Seat limits and downgrade behavior
Elite includes **five active seats total**, including the owner. An unexpired pending invitation
reserves a seat; suspended members do not.
If the workspace falls to a one-seat plan, Y2 keeps the owner active and suspends the remaining
members in join order. Suspended members:
- cannot use the organization as their active workspace;
- disappear from their workspace switcher;
- retain a membership record for possible restoration.
When the workspace returns to a plan with more seats, Y2 reactivates suspended members in the same
deterministic order until the new cap is full.
## Govern an Elite organization
Owners and admins can open **Settings → Governance** to review:
- active seats, API-key count, current-period event count, and plan limits;
- per-member event counts and cost units for the current UTC month;
- every workspace API key's prefix, scopes, state, request count, creator, expiry, and latest use
time and IP;
- the latest 50 workspace audit records.
The audit log records workspace membership actions such as invite creation, acceptance, approval,
revocation, role changes, and member removal. It also records material Project and Automation
changes with the acting user's identity, including Project lifecycle and context changes,
evidence and graph actions, Automation definition changes, and manual run requests. It is not a
complete log of every report, chat message, model call, or API operation.
Projects and Automation definitions remain owner-private in this release. Audit visibility gives
Elite administrators governance evidence; it does not grant access to another member's private
Project content.
## Workspace API keys and limits
New API keys require a Pro or Elite active workspace. Keys created in an organization are scoped to
that organization's resources and can be managed only by owners or admins.
Elite organization limits are shared across the tenant:
| Limit | Elite value |
| --- | ---: |
| Active seats | 5, including the owner |
| Total profiles | 50 |
| API keys | 25 |
| API requests | 120/minute and 50,000/day |
| Chat budget | 5,000 credits/month |
Creating or rotating more keys does not multiply the workspace's aggregate request limits or usage
budget.
## Delete an organization
In **Settings → Workspace**, an owner or admin can delete the active organization after typing its
name. Personal workspaces cannot be deleted.
Deletion is blocked while the organization still has an active or unresolved billing subscription
or tenant resources such as profiles, reports, delivery records, webhooks, keys and usage logs,
chat artifacts, investigations, OSINT workspace data, or other dependent records. Clear the
checklist shown in the app first. Large cleanup sets can require support rather than an in-app
deletion.
After the blockers are cleared, deletion removes the organization record, memberships,
invitations, audit logs, and workspace rate counters. Affected users fall back to another active
membership when one exists.
## Related guides
Compare workspace features, seats, API keys, and usage limits
Create and protect workspace-scoped credentials
Understand profile ownership and workspace capacity