API documentation

API reference

The /v1 surface is the versioned, read only API for customer machines. It has 39 endpoints, 37 GETs and two deliberate POSTs with pure read semantics. Every endpoint authenticates as described under Authentication, answers plain JSON, and changes nothing in your workspace. The same data is also served to AI assistants over MCP.

This page is the guide. The field-level reference is the live OpenAPI document at https://api.stonewake.ai/openapi.json: the machine-readable schema for every response, request parameter, and error shape. The /v1 routes are tagged v1 within it, and the deprecated twin below carries deprecated: true there.

Two id spaces appear below and never mix on one route: node_id is a graph node id, the id /v1/search hits carry and every /v1/nodes/... route speaks; entity_id is the closed institution registry's own id space (/v1/entities).

Identity and team scope

  • GET /v1/me: the calling credential and the teams its reads cover. credential is api_key or user, and team_scope holds all_teams and the teams it names, each with its id and name.

A workspace can hold several teams, for example one per desk, and each team keeps its own book. A key reads exactly one team, the one it was issued for (see Keys and access management). A key issued without a team, and every key issued before a team could be chosen, reads the workspace's default team. all_teams is true only for a signed in administrator or compliance officer, never for a key. Every row of the deal book, the portfolio, the status change feed, the screenings list, the research runs and the developments feed carries team_id and team_name, so a reader can always tell whose book a row belongs to. A screening whose team_id is null is a workspace wide screening that every team reads.

A research run belongs to the team that started it. A key, like every reader of one team, reads only its own team's research runs and their developments, and another team's run_id answers 404 exactly like an unknown one. A signed in administrator or compliance officer reads the research runs of every team. A run whose team_id is null predates team scopes, and only those cross team readers read it.

Portfolio

  • GET /v1/portfolio: the monitored book, one item per exposure with its current status, score, top signals, last status change, and standing credit review (review, null when never reviewed, and review_overdue once its next review date has passed). Ordered by severity, red first. include_inactive=true includes deactivated exposures.
  • GET /v1/portfolio/summary: status counts over the active book, transitions_30d, the number of transitions in the last 30 days (every entry of the feed, an exposure's first assessment included), status_changes_30d, the status changes among them (an exposure's first assessment is an addition to the book, not a change), and reviews_overdue, the active exposures whose standing review date has passed.
  • GET /v1/portfolio/transitions: the status-change feed, newest first. since (an ISO 8601 timestamp) bounds the feed. Each row carries its cause, derived from the two assessments it compares: kind (added for an exposure's first assessment, else change), available (false when either assessment cannot be read, and then no driver is claimed), the drivers (the signals whose change moved the status, with their band before and after, the override the new run applied, and their citation ids; when only a driver's override changed, its band before and after is the same), more_drivers, stopped_scoring (how many drivers, named or counted, had a band before and none now), rules_changed, both assessment ids, and record, the register record behind the lead driver when one stands behind it. See Credit reviews and status changes.
  • GET /v1/portfolio/{exposure_id}/reviews: one exposure's credit review history, newest first, so the first item is the standing review. Each review carries its disposition (no_action, watch_list, intensified_care or escalated), note, next_review_on and reviewed_at. The history is append only. Owners and reviewers are member identities and are not served here.
  • POST /v1/portfolio/import: preview a book import. The body carries the book as CSV text in csv (at most 2,000 rows); every row is matched by its register identifier alone and answered with its outcome for your team's book and the reason, the rows paged by limit (at most 100) and offset while counts and total cover the whole file. dry_run is always true here, so nothing is written, and applying an import happens in the dashboard. The file format and the outcomes are described under Book import.

Entity risk

  • GET /v1/nodes/{node_id}/risk: your team's current risk assessment for one company, with the scored signal rows and their citations. The response carries the company's name (the same name /v1/portfolio rows serve as label) and distinguishes not held, held but not yet assessed, and assessed.

Signal rows carry a band of red, amber, green, or null. A null band means the signal could not be scored from the data available for this company; the row still serves, with its note saying why, so what was not observable is stated rather than silent. Unscored rows are what hold an assessment's completeness below 1, and evidence_completeness counts only bands backed by actual evidence (never bands a conservative rubric assumed for missing data), which makes it the stricter of the two figures. The insufficient status floor reads reachable_share, described below. The insolvency petition row reads preliminary insolvency measures ordered by a German court as a pending petition, with the band amber and the publication as its source, as it reads a winding up petition in the United Kingdom. When the insolvency petition row finds nothing in Germany, that means no such order was published, not that no petition is pending, because a court must publish an order only when it restricts the debtor's powers. A green row on a register event signal, whether or not it cites events, stands on a register feed that was drained within its refresh interval plus a six hour grace and harvested back to the start of the signal's window; where a feed's harvest does not reach that far back yet, the row stays unscored and its note states how far back the harvest reaches. For every row SEC EDGAR serves, the going concern doubt row included, SEC EDGAR also has to have been searched in full since the company was linked to its SEC EDGAR identifier; until then those rows stay unscored and their note says so. Companies House charges reads companies one by one, so a green new charge activity row also needs the charges sweep to have read this company's full list of charges within that interval (a company with no charges counts too, while a check of a single charge or one streamed charge never does), and it covers the full two years only once the held charge history has been loaded; until then it stays unscored and its note says so.

Every signal row also names its state, one of evidenced, register_quiet (a connected register was read and recorded nothing), gap, awaiting_bank_input or not_applicable. A gap says why in gap_reason, for example register_not_integrated when no source we connect serves that signal for the company yet, or no_public_register when the company's jurisdiction keeps no public register for it. A gap no free nationwide register can close reads paid_register_excluded (in the United States each state keeps its own company and lien registers, and federal court records are sold per page through PACER), and because paid registers are outside our sources by policy it counts as explained, as no_public_register does. A scored row names its evidence grade, from A (a statutory register, filed accounts or a bank certificate) to D (a name only witness). A signal we cannot yet read for the company's jurisdiction is a gap, never not_applicable; that state is kept for signals that cannot exist for the company at all. The assessment carries seen_share (the weight share of applicable signals actually observed), explained_share (the seen share plus terminal gaps, which no free nationwide register can close), reachable_share (the observed share of the signals some connected source can serve for the company's jurisdiction) and coverage_line, one to three sentences that count the covered signals, the gaps with their reasons and the signals that do not apply. reachable_share is the figure the insufficient status floor reads. A signal no connected source reaches still counts against seen_share but never makes a company insufficient, and an amber floor that stands on grade A or B evidence keeps the status amber below that floor. /v1/portfolio rows carry all three shares.

A held company also carries entity_class: the class its checks route on (corporate, sovereign_or_public_body, bank, spv or fund), its source (analyst_override when your team set the class on the exposure in the dashboard, otherwise the register rule that derived it), its confidence, decided_at, and derived_class, the class the register facts alone give. A bank is never scored on corporate distress models: those rows and the corporate leverage ratios serve as not applicable even when the filed accounts compute them, and the check a bank needs instead (its capital and asset quality, not scored yet) serves as a row with a null band and a note saying so. The weight of the rows it stands in for still counts in completeness, evidence_completeness and the three shares, so a bank never reads better covered than the same company unclassified. A corporate model row no connected source reaches for the bank's jurisdiction stays the gap it is for the same company unclassified. A class rule never makes a status read better on its own either. The one exception is a bank whose red or amber came from corporate models. It may read better than the same company unclassified, but never better than insufficient while its capital check is not scored. That exception therefore never reads green. A bank reads green only when the same company reads green unclassified. The class is read only here; your analysts set it in the dashboard.

  • GET /v1/entities/{node_id}/risk: deprecated twin of the route above, kept for existing callers and marked deprecated: true in the OpenAPI schema. New integrations use /v1/nodes/{node_id}/risk.
  • GET /v1/risk-assessments/{assessment_id}: one assessment by id. Assessments are append-only history, so the id is a stable permalink.

Scoring rubrics

  • GET /v1/risk-profiles: your team's rubric versions, newest first.
  • GET /v1/risk-profiles/{version}: one version with its full configuration.
  • GET /v1/risk-config: the rubric currently scoring your team's exposures. version is null while the built-in default is live.

Institution registry

  • GET /v1/entities: the institution registry (export credit agencies, multilaterals and banks, plus the trade publications deals cite, marked kind trade_press), in name order.
  • GET /v1/entities/{entity_id}: one registry row's identity.
  • GET /v1/search?q=...: find a company by name or identifier (lei:5493..., a bare registration number, or a name). Hits carry the node_id the node routes speak, plus jurisdiction and registration data. connected_sources names the sources the query ran against, so an empty result is attributable. limit caps at 25.

Search answers its own envelope, not the standard list envelope: { "query": ..., "hits": [...], "connected_sources": [...] }, with no limit, offset, or total keys.

Company nodes

  • GET /v1/nodes/{node_id} answers one company node's identity (names, jurisdiction, and register identifiers) with every current register record as a citation, newest first. records_total counts records, and registers states each register's count as records_total, the register with the newest record first. The response is streamed as it is read, and it grows with the company, because a company listed in a securities register can hold over 100,000 records, tens of megabytes of citations. While other reads of very large companies are running, one more can answer 503 server_busy; see Errors and rate limits. A response still streaming after 60 seconds is cut off, so read a very large company through the records route below.
  • GET /v1/nodes/{node_id}/records is the efficient way to read a large company. It pages the node's current records as citations, newest first, in the standard list envelope. Without connector_id it pages every register in the order of the node read's records; with connector_id, as the registers rows carry it, it pages one register alone. total is the exact count of what is paged, and every page also states records_total, the node's count, and registers, each register's count, so the first page tells a client which registers to read.
  • GET /v1/nodes/{node_id}/financials: the node's financial facts, each row citing its source record. A row with derived true is not stated in the cited filing. It was calculated from two figures that filing states, and derivation says how in plain words. Every other row is served as filed.
  • GET /v1/nodes/{node_id}/events: the register-event feed on the node, grouped and tier-ordered, each row citing its evidencing record where one exists. group expands one rollup into its member rows. A German insolvency publication takes its tier from the procedural step the court published: an opening, preliminary measures and a petition rejected for lack of assets are alert rows, and later publications in a proceeding are review rows. A member event whose attrs.history_backfill is true was loaded from register history the platform already held rather than published on the day it arrived, and its occurred_at is still the date the register states; on every other event the field is null.

Each entry in a node's records carries verify_status, the outcome of the latest check of that record against its source. ok means the source still serves the record as stored. gone means the source positively no longer serves it; the record stays for review and is never deleted. unreadable means the latest check reached the source, but its answer proved neither the record nor its absence, so nothing about the record changed. If its node is watched, such a record is checked again at every refresh of the watch, daily or weekly by the watch's cadence. Any other such record is queued behind every record not in this state and is checked again only when a verification run has capacity left after all of them. null means no check has been recorded for the record.

Financing

  • GET /v1/nodes/{node_id}/financing: the node's debt surface: registered charges, listed debt instruments, debt-position facts, and per-source coverage states, so what was not searched is stated rather than silent.

Ownership

  • GET /v1/nodes/{node_id}/ownership-chain: the node's upward ownership chains, hop by hop, each hop cited. Person owners are never named: a chain that reaches a person ends there with an explicit gated terminal.

Register watches

  • GET /v1/watches: your workspace's register watches, newest first: the company nodes whose register data it keeps fresh. A register watch scores nothing; credit monitoring is the portfolio above.

Screenings

  • GET /v1/screenings: your workspace's adverse-media screening runs over organizations and countries, newest first. Filters: entity_id, node_id, iso3, subject (the subject's name, matched on its normalized form), and subject_type (entity or country).
  • GET /v1/screenings/{screening_id}: one screening with its per-category assessments, findings, and cited press quotes.
  • GET /v1/deals/{deal_id}/screening-rollup: the screening picture across one deal's parties.

Deals

  • GET /v1/deals: the ranked deal book. Filters: stage, window, country_code, region, sector, status, eca, eca_named, min_value_usd, and q; sort is one of score, last_activity, value, first_seen, and every sort orders by the ranking window first and the chosen key inside it. The rows a desk holds back by default (score_hidden true: closed, cancelled, older than the stale window of 18 months by default, own bank, domestic) are off the page unless include_hidden=true.
  • GET /v1/deals/{deal_id}: one deal with its parties, its deal summary and the cited timeline. summary_lines holds three lines generated from the cited findings by fixed rules, not by a model (who and what, the stage a source confirmed, why it matters for the bank), each with citations naming the field, the linked finding and the verbatim quote behind it; finding_id is null on the citation of the ranking's stored mandate reason. next_action is the analyst's own step only. timeline_limit caps the timeline rows, up to 200.
  • merged_into on a deal names the deal this one was folded into after it turned out to be the same transaction. It is null on every standing deal; when it is set, read the deal it names and treat this row as history. Folded rows keep their timeline and their events, and they are dismissed, so they appear only when status asks for dismissed deals.

Every deal row carries how it was ranked. score_window is the window it was partitioned into, with score_window_label as the word a reader sees: financing open, early stage, not confirmed, financing closed, cancelled. The score is a priority INSIDE that window, never across the book, so the two are read together: a closed deal scoring 90 and an open one scoring 60 are not comparable numbers, and the list never places the closed one above the open one. score_band is the band word shown before the number, score_reason_count how many reasons stand behind it, and score_hidden marks the portfolio rows a desk holds back by default (closed, cancelled, older than the stale window, own bank, domestic; a stale row names its date in the demotions, "Event dated 1 Mar 2023, older than 18 months; held back"). score_own_bank marks the rows where your own bank is named as a lender, read from the stored demotion sentence; the Deal Book shows them as Your bank is in. The response for one deal adds score_reasons: the band word, the window, one plain sentence per scoring component saying which fact it matched, and the demotions applied by name.

Every row also carries what the surfaces derive from its stored facts, so a client reads the same words the dashboard, the PDF and the workbook show. state and state_label are the confirmed state over the deal's linked findings and its word (early news, planned, tender open, contract awarded, arranger mandated, financing being arranged, cover in principle, cover agreed, signed or closed, cancelled, not confirmed); null means no source's quote confirmed a state. event_date is the newest event date a source stated in its own words; null means none was stated and last_event_at, the date published, is the fallback. lenders lists the lender names on the deal in name order, the third party group the Who is in column shows beside the agencies and the exporters. angle says why the row is on the desk's list (an export credit agency named, an exporter named, a buyer named, or none yet) and ask what is still to be financed, derived from the confirmed state, the window, the named agencies and the named lenders (a lender named with no agency at an early stage reads Lenders named, cover open). A row whose state no source confirmed reads State not confirmed by a source yet, so the ask never names a step or an open role that no quote carried. Timeline entries carry state, state_label, state_quote (the passage that confirmed the state, capped like every quote) and event_date, each null where nothing was confirmed.

Research

  • GET /v1/monitoring/targets: the research targets, the organizations research checks on a cadence, each with its latest run outcome and next due time.
  • GET /v1/monitoring/catalog: your desk's verified research catalog.
  • GET /v1/monitoring/sources: the sources research reads.

Countries and reference data

  • GET /v1/countries: the country index.
  • GET /v1/countries/{iso3}: one country's cited detail: indicators, risk breakdown, and related deals. Every indicator row carries its dataset attribution.
  • GET /v1/reference/datasets: the reference datasets behind the country data, with licence and attribution lines. A dataset whose terms do not permit republication is withheld across the surface rather than served unattributed.

Geography

  • GET /v1/geography/summary: the live deal book rolled up by region and country: counts, values, and risk scores.
  • GET /v1/geography/events: the geographic event feed from reference lists, newest first.

Runs and developments

  • GET /v1/runs: your team's research runs, newest first, each naming its team.
  • GET /v1/runs/{run_id}: one run with its grounded findings, sources, and cited press quotes.
  • GET /v1/developments: the grounded developments feed from your team's research runs. sort is recent or score.

Citations

  • POST /v1/citations/resolve: one of the surface's two POSTs, with pure read semantics: nothing is created, changed, or enqueued. The body carries id lists (record_ids, event_ids, assessment_ids; up to 100 ids each), and every requested id answers either its resolved evidence line or an explicit null. Unknown ids and ids this surface may not serve are indistinguishable. A resolved event carries headline, the sentence the event feed shows for it.

A citation whose source_url can only open a search form, not the record itself, carries locator: the coordinates to search it by, as text. The German insolvency portal publishes no link per notice, so its citations name the court, the case number and the publication day. locator is null wherever the link opens the record.

Pagination

List endpoints accept limit and offset and answer one envelope:

json
{ "items": [], "limit": 50, "offset": 0, "total": 0 }

total is the exact count after filtering. The maximum limit is 100, except /v1/search, which caps at 25 hits and answers its own envelope (see Search). offset is a whole number from 0 to 9223372036854775807; a larger value answers 422. The records route of a company node carries records_total and registers beside these keys.

Errors

Every 4xx from a /v1 endpoint carries the structured envelope described under Errors and rate limits, except a 422 validation refusal, whose body names the offending parameter or field instead. A 404 is canonical: it never confirms whether a resource exists outside your workspace.

Posture

  • Read-only: every endpoint is a GET, except POST /v1/citations/resolve and POST /v1/portfolio/import, whose semantics are a pure read.
  • Companies and countries only: person data never appears on this surface, whatever your workspace's settings. A person-subject lookup answers the same canonical 404 as an unknown id.
  • Cited: responses that carry findings carry their source citations. Press quotes are capped at 240 characters, one passage per source, always with the source URL and attribution; a quote that cannot be attributed is not served.
  • Ranked by publisher: every cited source carries source_tier and tier_label. Tier 1 is a primary source, the party to the transaction or the authority recording it; tier 2 is trade press; tier 3 is an aggregator, which is also where a publisher we have not classified lands. A source whose text came from a search index rather than the page carries text_provenance of snippet and counts as one report whose page is not verified.

Versioning and deprecation

The version is the path prefix: /v1 is the current version, and the guarantees below are scoped to it.

  • Within v1, evolution is additive. More endpoint families will be added, and response fields and error codes may be added; existing routes, fields, and error codes are never renamed, removed, or retyped within v1. Write clients to ignore fields they do not recognize.
  • A route being replaced is marked deprecated: true in the OpenAPI document and stays functional before any removal; the deprecated twin under Entity risk is the live example.
  • Breaking changes ship only under a new version prefix, never in place within v1, and with advance notice to affected customers.
  • Planned maintenance is announced in advance where feasible, and material operational changes are communicated to affected customers.