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.credentialisapi_keyoruser, andteam_scopeholdsall_teamsand theteamsit names, each with itsidandname.
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, andreview_overdueonce its next review date has passed). Ordered by severity, red first.include_inactive=trueincludes 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), andreviews_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 itscause, derived from the two assessments it compares:kind(addedfor an exposure's first assessment, elsechange),available(false when either assessment cannot be read, and then no driver is claimed), thedrivers(the signals whose change moved the status, with their band before and after, theoverridethe 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, andrecord, 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 itsdisposition(no_action,watch_list,intensified_careorescalated),note,next_review_onandreviewed_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 incsv(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 bylimit(at most 100) andoffsetwhilecountsandtotalcover the whole file.dry_runis 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'sname(the same name/v1/portfoliorows serve aslabel) 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 markeddeprecated: truein 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.versionisnullwhile 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, markedkindtrade_press), in name order.GET /v1/entities/{entity_id}: one registry row's identity.
Search
GET /v1/search?q=...: find a company by name or identifier (lei:5493..., a bare registration number, or a name). Hits carry thenode_idthe node routes speak, plus jurisdiction and registration data.connected_sourcesnames the sources the query ran against, so an empty result is attributable.limitcaps 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_totalcountsrecords, andregistersstates each register's count asrecords_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 503server_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}/recordsis the efficient way to read a large company. It pages the node's current records as citations, newest first, in the standard list envelope. Withoutconnector_idit pages every register in the order of the node read'srecords; withconnector_id, as theregistersrows carry it, it pages one register alone.totalis the exact count of what is paged, and every page also statesrecords_total, the node's count, andregisters, 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 withderivedtrue is not stated in the cited filing. It was calculated from two figures that filing states, andderivationsays 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.groupexpands one rollup into its member rows. A German insolvency publication takes itstierfrom the procedural step the court published: an opening, preliminary measures and a petition rejected for lack of assets arealertrows, and later publications in a proceeding arereviewrows. A member event whoseattrs.history_backfillis true was loaded from register history the platform already held rather than published on the day it arrived, and itsoccurred_atis 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), andsubject_type(entityorcountry).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, andq;sortis one ofscore,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_hiddentrue: closed, cancelled, older than the stale window of 18 months by default, own bank, domestic) are off the page unlessinclude_hidden=true.GET /v1/deals/{deal_id}: one deal with its parties, its deal summary and the cited timeline.summary_linesholds 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 withcitationsnaming the field, the linked finding and the verbatim quote behind it;finding_idis null on the citation of the ranking's stored mandate reason.next_actionis the analyst's own step only.timeline_limitcaps the timeline rows, up to 200.merged_intoon 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 whenstatusasks 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.sortisrecentorscore.
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 explicitnull. Unknown ids and ids this surface may not serve are indistinguishable. A resolved event carriesheadline, 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:
{ "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/resolveandPOST /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_tierandtier_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 carriestext_provenanceofsnippetand 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: truein 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.