Docs
WorldSignals turns official registers, journals and public feeds into one stream of standard events. This page covers what is live today.
signup, key, events and watch), the remote MCP server, signed webhooks and the Account page are live. For AI assistants: llms.txt, llms-full.txt, openapi.json.Quick start
Sign up (free in early access) and confirm your email from the link we send. Work emails get in as soon as they confirm: the link signs you in and shows your access key. Personal addresses (Gmail, Outlook…) wait for our daily approval; we email you when you’re in. Each key is shown once, when it is made. Lost it? Sign in and open your Account page: it emails you a link for a new key (the old one stops working when you use it) and lists the apps you connected. Then read the newest collected events:
curl -H "Authorization: Bearer $WS_KEY" "https://worldsignals.dev/api/events?types=company.registered&country=ES&limit=5"
Without a key, GET /api/events/preview shows the newest events reduced to type, place and day.
Every event follows the event standard. Deduplicate on id; the same fact always has the same id.
Events API Live
GET /api/events
Collected public events. Needs Authorization: Bearer <access key> (or the sign-in cookie in a browser); without it the answer is 401.
| Parameter | Meaning |
|---|---|
types | Comma-separated event types, for example company.registered,company.dissolved. Unknown types are ignored. |
country | An ISO 3166-1 alpha-2 code, for example ES, or several, comma-separated: ES,FR. XX is the events about the whole world (vulnerabilities, outages, AI models): add it to get them too, ES,FR,XX. Without country, every country, XX included. |
after | An event number: the events stored after it, oldest first. Poll with the cursor from the last answer, and ask again straight away while more is true. Or an RFC 3339 time: the newest events first seen after it (observed_at), for a quick look. |
before | An event number from before in the last answer: older events, newest first. Use it to page back through everything that matches. Not with an event number as after. |
limit | 1 to 200, default 50. |
Add the filters below to narrow by platform, ecosystem, tag or any field. The answer is {"events": [...], "total": n, "cursor": n, "more": false}. total counts every public event kept (30 days); cursor is the number to send as after next time: the newest event in the answer, or, when the answer isn't full, the newest event stored, so the next poll skips what didn't match your filters. Every stored event has a number, in the order it was stored, so polling with it never skips one. Unless after is an event number, the answer also has before and older: while older is true, ask again with before to page back to older matches.
cursor=$(curl -s -H "Authorization: Bearer $WS_KEY" "https://worldsignals.dev/api/events?limit=1" | jq .cursor)
# later, and on every poll after that:
curl -s -H "Authorization: Bearer $WS_KEY" "https://worldsignals.dev/api/events?after=$cursor&limit=200"
GET /api/stream Live
The same events, live, as Server-Sent Events: any EventSource or curl -N reads it, and ws watch is built on it. Each event’s SSE id is its number. Reconnect with Last-Event-ID (or ?after=) and the stream carries on after it, so a laptop that slept or a dropped network misses nothing within the 30 days kept. Without one it starts at the newest event.
| Parameter | Meaning |
|---|---|
types | Comma-separated event types, or all. Default: the types you chose when signing up. |
countries | Comma-separated ISO codes, or all. Default: the countries you chose. XX is the events about the whole world (vulnerabilities, outages, AI models), a country like the others. |
platforms, ecosystems, tags, where | The filters, as for /api/events. |
curl -N -H "Authorization: Bearer $WS_KEY" "https://worldsignals.dev/api/stream?types=vulnerability.exploited"
retry: 3000
event: ready
data: {"after":51234,"head":51234,"types":["vulnerability.exploited"],"countries":[]}
: hb
id: 51240
data: {"specversion":"1.0","id":"…","type":"vulnerability.exploited",…}
event: bye
data: {"after":51240}
When the countries leave out XX but a type you asked for only comes as XX, ready says so in hint, as the events API does. A connection stays open for a few minutes, then ends with bye; reconnect at once (EventSource does it for you). A heartbeat (: hb) comes every 15 seconds and means everything waiting has been sent. gap warns that events after your number are past the 30 days kept. An id: line with no data means nothing up to that number matched your filters: keep it as your place (EventSource does), and a reconnect skips those events.
Limits
Each access key can make 120 requests a minute, shared by /api/events, /mcp, /api/me and webhook tests, and open 12 connections a minute to /api/stream; a stream that reconnects every few minutes uses one. Over a limit the answer is 429 with Retry-After: 60, and ws watch waits and reconnects on its own.
Filters
Both /api/events and /api/stream narrow events beyond type and country. Values in one filter are alternatives (any of them); different filters all apply.
| Parameter | Meaning |
|---|---|
platforms | Operating systems in data.platforms: windows, macos, linux, android, ios, chromeos, freebsd. |
ecosystems | Package registries in data.ecosystems, which stand for languages: npm (JavaScript), pypi (Python), maven (Java), nuget (.NET), go, rubygems (Ruby), crates (Rust), packagist (PHP), pub (Dart), hex (Elixir, Erlang), swift, cocoapods, github_actions, and wordpress for its plugins. |
tags | Any of these tags, for example critical, exploited, ransomware. |
where | A condition on any field: a dotted path, an operator (= != > >= < <=) and a value. | separates values for = and !=. Numbers compare as numbers, true and false as booleans, and an array field matches when any item does. Repeat it, up to eight times; all must hold. |
Vulnerabilities, advisories, exploits and package releases carry platforms and ecosystems. New CVEs get them from the affected products once the CVE has been analysed and, before that, from the description and the authority that assigned it, so expect a few broader matches. Advisories get them from their packages, releases from their registry.
With field filters, one answer reads at most 5,000 event numbers, so a poll can come back with no events, more: true and a later cursor: keep asking from the cursor until more is false. The stream and ws watch do this for you. Newest first works the same way backwards: keep asking with before while older is true.
# Critical and high CVEs that affect Windows
curl -H "Authorization: Bearer $WS_KEY" "https://worldsignals.dev/api/events?types=vulnerability.published&platforms=windows&where=data.cvss.severity%3Dcritical%7Chigh"
# Python advisories, live
ws watch --type vulnerability.advisory_published --ecosystem pypi
# Vulnerabilities exploited by ransomware on Windows
ws watch --type vulnerability.exploited --tag ransomware --platform windows
GET /api/stats
Counts of what has been collected: events (public events kept, 30 days), the sources and countries they came from, the types seen and the time of the latest event, plus the number of connectors, of eventTypes defined, and retentionDays.
GET /api/catalogue
Every event type with its family, visibility, an example event, and the connectors that collect it today (empty while a type is planned).
GET /schemas/<name>/1.json
JSON Schema (draft 2020-12) for the envelope, the shared definitions and each typed payload, for example company.registered/1.json.
Get events into your agent
Two ways in today. Live: ws watch on a computer or server that stays on starts your agent the moment events arrive. Polling: an agent that runs on a schedule asks for what is new since last time. Cloud agents that can’t run a program of their own get events through webhooks or the MCP server, poll on a schedule, or let ws watch wake them through their own trigger.
| Agent | Live | Polling |
|---|---|---|
| Claude Code | ws watch --agent claude runs claude -p for each event or batch | A routine on a schedule runs npx worldsignals events --after …; or ws watch --once from cron |
| Codex | ws watch --agent codex runs codex exec in a read-only sandbox | A scheduled task runs npx worldsignals events --after … (allow worldsignals.dev in its network settings) |
| Grok Build, Muse Code and other terminal agents | ws watch --exec runs the agent’s own headless command with the events on stdin | ws watch --once from cron |
| Claude Code routines, Grok Bot routines | ws watch --exec calls the routine’s webhook or API trigger with the events | A schedule trigger that reads /api/events?after= |
| Cursor, VS Code, GitHub Copilot, Claude Managed Agents | Webhooks to an endpoint that starts the agent | The MCP server with your key: search_events with the last cursor |
| Claude Duet, dots, Muse | Through the MCP server and webhooks, once they are listed for them | An in-app schedule, once the MCP server is listed for them |
| Your own code | GET /api/stream with any SSE client, or ws watch --exec, --out or plain JSON lines | GET /api/events?after=<cursor> |
Claude Code
export WS_KEY=wsk_…
npx worldsignals watch --type vulnerability.exploited --agent claude --prompt "Check whether any of these affect the dependencies in this repo, and say which."
It runs in the folder you start it from. claude -p can’t stop to ask for approval, so it only uses tools that need none (reading files) unless you allow more after --, for example -- --allowedTools "Read,Grep". Add --batch to run once for events that arrive together.
On a schedule instead (a Claude Code routine, or any cloud run without a disk that remembers): ask for events first seen in the last interval plus a margin, and skip ids you have already handled.
npx worldsignals events --type vulnerability.exploited --after "$(date -u -d '-70 min' +%Y-%m-%dT%H:%M:%SZ)" --json
Codex
npx worldsignals watch --type software.released --agent codex --batch --prompt ./release-notes-brief.md -- --skip-git-repo-check
Runs codex exec --sandbox read-only with the prompt on stdin. Pass another --sandbox after -- to let it write. --prompt takes the text or a file.
Grok Build, Muse Code and other terminal agents
Any agent with a headless mode that reads a prompt on stdin works through --exec. Check its docs for the flag; the events arrive as JSON lines on stdin, and WS_EVENT_TYPES and WS_EVENT_COUNT are set.
npx worldsignals watch --type service.outage_started --exec ./ask-my-agent.sh
Cloud agents with a webhook or API trigger
Claude Code routines have an API trigger and Grok Bot routines a webhook trigger. Neither takes our signed webhooks as they are, so a small script run by ws watch --exec passes the events on. Take the URL, token and any required headers from the trigger’s settings.
#!/bin/sh
# fire.sh: send the waiting events to a cloud agent's trigger. The events come on stdin.
jq -Rs '{text: .}' | curl -fsS -X POST "$TRIGGER_URL" -H "Authorization: Bearer $TRIGGER_TOKEN" -H 'content-type: application/json' --data-binary @-
npx worldsignals watch --type procurement.tender_published --country PT --batch --exec ./fire.sh
Batch them: triggers have hourly limits, and one run for many events costs less.
Polling from cron or a scheduled job
ws watch --once handles everything since the last run, then exits. It remembers its place in --state, so no event is skipped or handled twice:
*/15 * * * * WS_KEY=wsk_… npx worldsignals watch --once --state ~/.ws/tenders.json --type procurement.tender_published --out ~/tenders
From your own code, poll GET /api/events?after=<cursor> and keep the cursor each answer gives (Events API).
Event format
A CloudEvents 1.0 envelope with WorldSignals fields: time (when it happened), observed_at (when we first saw it), country and place, the subject and other entities, a one-line title, the typed data, and provenance with the source record link, licence and attribution.
- The event standard: every field and the rules for money, industry codes, places and people.
- The event catalogue: 84 types, with an example of each and whether it is collected today.
Sign up from an agent Live
An AI agent can sign up the person or company it works for. The agent asks; the person confirms. Nothing is signed up until they do, and a person who is already signed up keeps everything they chose.
curl -X POST https://worldsignals.dev/api/signup-requests -H 'content-type: application/json' -d '{"email": "ana@example.com", "company": "Example Lda",
"eventTypes": ["company.registered"], "countries": ["ES"],
"plan": "developer", "billing": "monthly",
"agent": {"name": "My agent", "version": "1.0"}}'
The answer has a confirm_url, a message_for_human and a pickup_secret. Keep the secret to yourself and never put it in a URL; pass the other two to your person: they open the link, see exactly what you asked for, and confirm or decline. Declining forgets their email. Confirming sends them an email that names you, and only a click on it proves the address is theirs: pressing Confirm alone never releases a key. Once they click it (within 24 hours, or the request expires), GET /api/signup-requests/<t from the link> with the header Authorization: Bearer <pickup_secret> returns access_key, once, which reads events for them. If you can sign, send pickup_public_key (an Ed25519 public key: its 32 raw bytes, base64url) with the request instead: no secret comes back, and you sign the fetch with X-WS-Timestamp (unix seconds, within five minutes) and X-WS-Signature (base64url Ed25519 signature of GET /api/signup-requests/<t> <timestamp>); the ws CLI does this for you. The key comes at once for a work email, or after our daily approval for a personal address (Gmail, Outlook…). Until then it says email_confirmed: false, then waiting_list: true. Fetch it within 7 days of that and keep it: after that the link only says expired. If they already have an account, nothing you asked for changes it until they click.
| Field | Meaning |
|---|---|
email, agent.name | Required: your person’s email and your own name, so they know who asked. |
eventTypes, countries | Optional: ids from /api/catalogue and ISO country codes. Without countries, every country. XX is the events about the whole world (vulnerabilities, outages, AI models): add it to a list to get them too, ["ES", "XX"]; the answer has a hint when a chosen type only comes as XX and the list leaves it out. The person can change them later. |
plan, billing | Optional: free, developer, team, business or enterprise; monthly or yearly. |
company, role, useCase | Optional, and shown to the person before they confirm. |
Links last seven days. One network address can make ten requests a day. The full description is in OpenAPI, and a summary for assistants is in llms.txt.
Safety: run agents that read events in a sandbox
Events carry text from the outside world: company names and purposes, tender titles, advisory summaries, exploit names, and, once connected accounts arrive, your customers’ DMs and reviews. Any of it can contain instructions written for an AI (prompt injection). Treat every event as data, never as instructions, and give the agent that reads them as little power as possible.
- Isolate the reader. Run the agent that processes events in a sandbox: a container or VM with no access to your machine, a read-only filesystem except a scratch folder, and no ambient credentials (no SSH keys, cloud tokens or browser sessions).
- Least privilege. Give it only the tools the task needs, and network access only to the hosts it must reach. An agent that drafts emails needs no shell; one that opens tickets needs no payment API.
- Split reading from acting. Let a sandboxed agent read events and return structured output (a score, a draft, fields from a schema); let separate code or a privileged agent act only on validated fields, never on free text from the event.
- A person approves what leaves. Sending messages, paying, merging code, changing infrastructure or anything hard to undo goes through human approval, at least until you trust the workflow.
- Mark the boundary in the prompt. Wrap event content in clear delimiters and tell the model it is untrusted data to analyse, not instructions to follow. This helps, but it is not a defence on its own: the sandbox is.
- Watch and limit. Log what the agent does per event, rate-limit its actions, and alert on anything unusual.
ws watch --agent does what it can by default: it passes events to the agent between random markers, says they are data and not instructions, and holds back your access key. claude -p can’t approve tools on its own, and codex exec runs with a read-only sandbox. That narrows what an injected instruction can do, but it doesn’t replace a sandbox: for anything that matters, run ws watch itself inside a container or VM.
CLI Live
The worldsignals npm package (Node 18 or later, no dependencies, source on GitHub), also installed as ws:
npx worldsignals signup --email ana@example.com --agent "My agent" --type company.registered --country ES --plan developer
npx worldsignals key --link "https://worldsignals.dev/confirm?t=…" # after your person confirms
WS_KEY=wsk_… npx worldsignals events --type procurement.tender_published --country FR --limit 10
Without npm, download the same single file: curl -fsSLO https://worldsignals.dev/cli/ws.mjs and run node ws.mjs.
ws watch
Stays connected to the live stream and handles each event as it arrives. It remembers the number of the last event it handled, so after a restart, a sleeping laptop or a dropped network it carries on from there.
ws watch # one JSON event per line, your own choices
ws watch --type company.registered --country PT,ES --exec ./on-event.sh
ws watch --type procurement.tender_published --out ./events/
ws watch --type vulnerability.exploited --agent claude --prompt ./brief.md
ws watch --type software.released --agent codex --batch
| Option | Meaning |
|---|---|
--type, --country | What to watch, comma-separated (--country PT,ES). Default: what you chose when signing up. XX is the events about the whole world (vulnerabilities, outages, AI models): --country PT,ES,XX. |
--platform, --ecosystem, --tag, --where | The filters; ws events takes them too. Repeat --where for more conditions. |
--exec <program> | Runs it for each event (or batch), with the events as JSON lines on stdin and WS_EVENT_ID, WS_EVENT_TYPES, WS_EVENT_COUNT and WS_EVENT_SEQ set. Never through a shell; arguments after -- are passed as they are. Your access key is left out of its environment. |
--agent claude|codex | Starts claude -p or codex exec with your --prompt (text or a file) and the events, fenced off as data. Arguments after -- go to the agent. |
--out <folder> | Writes each event to its own file, named by number and type. |
--batch | Collects events and runs once for many: after a quiet --batch-idle (60 s), at most --batch-max-wait (600 s) after the first, or at --batch-max (100) events. |
--once | Handles what is waiting, then exits: for cron and scheduled jobs. |
--state <file>, --from now|start|<number> | Where it remembers its place (default ~/.config/worldsignals/watch.json; give each watcher its own), and where to begin instead. |
--timeout <seconds> | Stops a program or agent that runs longer (default 600). |
Events are handled one after another, oldest first, at least once: the place is saved only after the program or agent finishes, so one that was interrupted gets its events again. A program that fails is reported and the watch carries on.
How events reach your program
The same events, four ways. Events arrive in bursts, in quiet spells and in steady trickles, and the way you choose decides how often your program or agent runs.
For AI agents, use a batch or files. Each agent run takes time and costs money, and an agent that sees ten related events in one run decides better than ten runs that see one each. With --batch it runs once a burst is over, or after the longest wait you allow. With --out the events wait in a folder until your agent, a schedule or a person picks them up.
One by one
--exec ./handle.shRuns your program for every event, the moment it arrives.
Batches Best for agents
--exec ./agent.sh --batchCollects events and runs once: after 60 s of quiet, or after 10 min at most.
Files Best for agents
--out ./eventsWrites each event to its own file; whoever reads the folder handles them.
Lines of JSON
ws watchPrints one event per line, to pipe into anything.
--batch-idle, --batch-max-wait and --batch-max (events per run).Webhooks Live
Register an https endpoint with your access key and we POST each matching event to it, one event per request, in the order events were collected. Each request is a CloudEvents structured request (Content-Type: application/cloudevents+json; the body is the standard event) signed per Standard Webhooks.
curl -X POST https://worldsignals.dev/api/webhooks -H "Authorization: Bearer $WS_KEY" -H "Content-Type: application/json" -d '{"url":"https://you.example/hooks/ws","types":["vulnerability.published"],"where":["data.cvss.base_score>=9"]}'
# 201 {"id":"wh_…","secret":"whsec_…","filter":{…},"cursor":12345} the secret is shown only here
To verify a request, compute an HMAC-SHA256 of webhook-id.webhook-timestamp.body keyed with the bytes of your secret after whsec_ (base64-decoded), and compare its base64 with any v1, entry in webhook-signature. Reject timestamps more than five minutes old. webhook-id is the event id and stays the same on retries, so you can drop repeats.
import crypto from "node:crypto";
// body: the raw request body, exactly as received
export function verifyWebhook(secret, headers, body) {
const id = headers["webhook-id"], ts = headers["webhook-timestamp"];
if (!id || !ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = Buffer.from(secret.slice("whsec_".length), "base64");
const expected = Buffer.from(crypto.createHmac("sha256", key).update(`${id}.${ts}.${body}`).digest("base64"));
return (headers["webhook-signature"] ?? "").split(" ").some((entry) => {
const [version, sig] = entry.split(",");
const given = Buffer.from(sig ?? "");
return version === "v1" && given.length === expected.length && crypto.timingSafeEqual(given, expected);
});
}
Answer with any 2xx within 10 seconds; we don't follow redirects. Anything else is retried after 1, 5 and 30 minutes, then 2 hours, then every 12 hours. After three days of failures, or at once on 410 Gone, the webhook is switched off until you call POST /api/webhooks/<id>/enable, and delivery resumes where it stopped (within the 30 days we keep). A new webhook starts with the next event collected, or after the event number you pass as after. Filters are the same as the events API: types, countries (XX for the events about the whole world), tags, platforms, ecosystems, where; left out, types and countries are copied from your saved stream. Up to 5 webhooks; GET /api/webhooks lists them, DELETE /api/webhooks/<id> removes one, POST /api/webhooks/<id>/test sends a signed webhook.test event now, and POST /api/webhooks/<id>/rotate-secret issues a new secret, with requests signed with both for a day. Delivered events show in your key’s usage.
Plans and billing From 8 December
Everyone starts free. To upgrade, use the Plan panel on your Account page, or, from an agent or a script, ask for a payment link with your access key and pass it to your person: they pay with Stripe, the plan starts as soon as the payment goes through, and you never see a card.
curl -X POST https://worldsignals.dev/api/billing/checkout -H "Authorization: Bearer $WS_KEY" -H 'content-type: application/json' -d '{"plan": "team", "billing": "yearly"}'
plan is developer, team or business; billing is monthly (the default) or yearly. The answer has the url to open. ws upgrade --plan team does the same from the command line. POST /api/billing/portal gives a link to manage the subscription: change plan, card, invoices or cancel. VAT is added from the billing address; businesses can give their VAT number. Founding members, who signed up before 8 December 2026, keep 30% off for life while the subscription stays active. Until paid plans open, these answer 503 and everything is free.
Company timelines and watchlists Live
Every event is indexed by the companies in it, by registry number (gb_crn:09000000, fr_siren:938000004, us_cik:0000320193…), so one company’s events from every source come together. GET /api/entities?name=Acme&country=GB finds candidates; GET /api/entities/<key>/events is that company’s timeline, newest first (fuzzy=1 also joins on the name, marked weak). The MCP tool get_entity does the same.
A watchlist holds up to 1,000 companies, and you keep up to 10: POST /api/watchlists with {"name": "Suppliers"}, then POST /api/watchlists/<id>/entities with {"ids": ["gb_crn:09000000"]}. Add watchlist=<id> to the events API, the live stream or a webhook’s filter to get only those companies’ events.
Your own combined signals Live
Built-in combined signals join sources for you: one of them, a young company winning a public contract (combined.young_company_won_contract), is on every plan; the others are on the Team plan and up. Also on the Team plan and up, tell us which two events to watch for and we send a signal when both happen to the same company within a number of days: a company on your watchlist registers and wins a public contract within 90 days, or gets a licence and a building permit within 60. Companies are matched by their registry number, never by name. Team keeps 5 rules, Business 50. Each match is a private event, combined.custom_rule_matched, that reaches only you: through the events API, the live stream, webhooks and MCP.
curl -X POST https://worldsignals.dev/api/rules -H "Authorization: Bearer $WS_KEY" -H 'content-type: application/json' -d '{"name": "Young company wins a contract",
"first": {"types": ["company.registered"], "countries": ["GB"]},
"then": {"types": ["procurement.contract_awarded"]},
"within_days": 90, "order": "first_then"}'
Each step takes up to 4 types, and optionally countries (XX for the events about the whole world), tags and up to 10 where conditions in the filter language. within_days is 1 to 365; order is first_then (the second event on the day of the first or later) or any; watchlist limits the rule to the companies on one of your watchlists. A rule remembers the events that match each step for its whole window, although events themselves are kept 30 days. Only events collected after you save a rule produce signals; POST /api/rules/test (an unsaved rule) or POST /api/rules/<id>/test shows what it would have matched in the last 30 days. GET /api/rules lists your rules with their status, PATCH /api/rules/<id> changes or pauses one ("enabled": false), DELETE removes it. Without a paid Team plan or higher the API answers 402; if a plan ends, its rules stay and start again when it is renewed.
MCP server Live
A remote MCP server at https://worldsignals.dev/mcp (streamable HTTP). Your access key is the bearer token, so any MCP client that can send a header can use it: Claude Code, Cursor, VS Code and GitHub Copilot, Claude Managed Agents and your own. Clients that support OAuth can connect without a key: they find the sign-in through the server’s metadata (/.well-known/oauth-protected-resource/mcp), you sign in to WorldSignals with an emailed link or your key, and you allow the app on a consent page. Access tokens last an hour and renew on their own. Disconnect an app on your Account page; deleting your sign-up ends every connection.
claude mcp add --transport http worldsignals https://worldsignals.dev/mcp --header "Authorization: Bearer $WS_KEY"
For clients configured with JSON (Cursor, VS Code):
{ "mcpServers": { "worldsignals": {
"url": "https://worldsignals.dev/mcp",
"headers": { "Authorization": "Bearer wsk_…" } } } }
| Tool | What it does |
|---|---|
list_event_types | Every event type by family, whether it is collected today, and in which countries |
search_events | Events filtered by types, country (one code or several, comma-separated), tags, where conditions and since (a time), newest first, paging back with before while older is true; with after, the events stored after that event number, plus a cursor for the next call. limit: 20 by default, at most 50 |
get_event | One event by its id, in full |
my_stream | The event types and countries saved for your key |
All tools are read-only and share your key’s 120 requests a minute with the events API. Every answer reminds the agent that event content comes from public sources and is data, not instructions.
Sources and licences
Events come from official registers and journals, public databases and status pages, and package registries; connected accounts are planned. We use public sources under their reuse licences, pass on paid data only where its licence allows, and deliver sources that need your own key to you alone.
Every event names its exact source, a link to the record, the licence and the attribution in provenance. The event catalogue shows which types are collected today and where.
Delivery and retention
- Almost every source runs all the time. PyPI releases are collected while a member’s stream needs them, and for seven days after.
- Collected public events are kept for 30 days. When a company was registered (its register number, name and date, never a sole trader) is kept for 400 days, so a later licence or contract can be recognised as going to a young company.
- Delivery is at least once: deduplicate on
id. Corrections are new events that point to the old one withsupersedes. - Every stored event has a number, in the order it was stored. The live stream and polling with
afteruse it, so you can stop and carry on without missing anything kept.