GET/v1/openapi.json
This document
Reference
Eyesea tracks candidate marine pollution observed by satellite as cases, records the evidence gathered on each, and publishes an assessment of what that evidence supports. Reports made in the field, and the labels drawn from both, are licensed separately. Version 1.2.1.
A case is not a record of a detection. It is a record of an investigation into one candidate feature, opened when a detector identifies that feature in a satellite scene and maintained thereafter. Evidence is retrieved and recorded against the case over the hours and days that follow, and the assessment is derived afresh from whatever evidence exists at the time of each request. A case may therefore move from unresolved to a firmer status days after it was opened, and a conclusion drawn from it is valid only against the evidence then available.
The following hold for every case:
These terms carry distinct meanings throughout this reference and are not interchangeable.
detectioncaseevidencehypothesisassessmentconfidencedecisionverdictattributionCase 59f8d98a-296b-4c08-9a0e-5b73676e1e3c, opened 6 September 2026. The values below are those held in the ledger, and the case can be retrieved with aledger:read key.
Detection05:44 UTC, case opened 16:23
A Sentinel-1 acquisition over the Norwegian Sea (71.306 N, 16.450 E, 1,021 m water depth, 151 km from shore) yields a 177,791 m² dark feature. detector_score 0.986, p_oil_corpus 0.93. The case opens with status NEW. The detector output is retained for traceability and does not contribute to hypothesis scoring.

GET /v1/cases/{case_id}/preview for this case. The orange outline marks the detected feature. The chip is 448 by 448 and carries a scale bar, the calibrated and raw scores, the area and the acquisition time.Context16:31 UTC
Wind, sea state, seabed and recurrence evidence is retrieved. One explanation is weakened and none is established, and the status becomes UNRESOLVED. At 20:00 the look-alike rule set evaluates the case; no flag fires and the status is unchanged.
METEOROLOGICALModel wind of 4.3 m/s at acquisition falls inside the 2.5 to 15 m/s observability band, but the six-hour minimum was 1.8 m/s. A scene-wide calm does not account for the feature, and local calm patches are not excluded.OCEANOGRAPHICWater depth of 1,021 m and no reef within 5,000 km. No seabed or reef signature is indicated; fronts and internal waves are not excluded by depth.OIL_FILMOne prior detection within 5 km, 2.8 km distant, with no footprint overlap. This indicates recurrence in the area rather than a fixed feature, against a ledger that begins on 19 August 2026.Corroboration and review20:30 to 21:31 UTC
An independent detection and two independent readings are recorded.
OIL_FILMAn independent provider recorded seven slicks within 15 km and 36 h; the nearest is 4.6 km distant, 13.3 h apart, and 2.2 km² in area. This corroborates slick-like activity in the same water, not this outline.OIL_FILMTwo independent reviews each rank an oil-film mechanism first of four. The later review records three conflicts between image and data and identifies eight discriminators.Assessment
"assessment": {
"assessment_version": "assessment-rules-0.1",
"current_status": "UNRESOLVED",
"confidence": "high",
"confidence_basis": "2 supporting inputs beyond the detector, 1 of them observations, nothing contradicting",
"contested": false,
"primary_hypothesis": { "hypothesis": "OIL_FILM", "score": 2, "support": 2, "contradiction": 0, "review_rank": 1 },
"alternative_hypotheses": [ { "hypothesis": "METEOROLOGICAL", "score": 0 }, { "hypothesis": "OCEANOGRAPHIC", "score": 0 } ],
"untested_hypotheses": [ "ARTIFACT", "BIOGENIC_FILM", "SHIP_WAKE" ],
"missing_evidence": [
{ "evidence": "OPTICAL_COVERAGE", "status": "pending", "would_discriminate": ["BIOGENIC_FILM", "OIL_FILM"] },
{ "evidence": "ATTRIBUTION", "status": "withheld", "would_discriminate": ["VESSEL_SOURCE"] },
{ "evidence": "HUMAN_VERDICT", "status": "absent" } ],
"contradictions": [ { "kind": "data_conflict", "detail": "the reviewer flagged 3 contradiction(s) between image and data" } ],
"conclusion": "OIL_FILM (mineral oil film, source unspecified) is the best-supported explanation at high confidence: supported by cross detection, independent review; nothing on file contradicts it. Not excluded: METEOROLOGICAL, OCEANOGRAPHIC. Case status UNRESOLVED. Not a measurement and not confirmation of oil."
}Two inputs beyond the detector support OIL_FILM and none contradicts it, giving a confidence of high. The alternative hypotheses are outscored rather than excluded, and three remain untested. The conflicts recorded by the reviewer appear under contradictions although they do not alter the ranking.
32d6fcc1Contradicting evidence and an adjudicated outcomeA case in the northern North Sea on 22 August 2026 followed the same sequence with a different result. Wind of 7 m/s fell inside the observability band and above the threshold at which natural films persist, so the WIND evidence contradicted bothMETEOROLOGICAL and BIOGENIC_FILM rather than remaining neutral to them. No independent detection fell within the window. A review ranked an oil mechanism first and recorded six conflicts between image and data. Twelve hours after acquisition a human reviewer recorded a verdict of CONFIRMED_OIL.
confidence then reads confirmed, the conclusion states the verdict, and history carries the transition from UNRESOLVED toCONFIRMED_OIL with the verdict as its reference. Records preceding the verdict are retained unchanged.
Each case carries an assessment, derived from the evidence on record at the time of the request under the rule set named in assessment_version. Derivation is a read operation: no records are written, no model is invoked, and no information is introduced beyond what the ledger holds. Each entry references the record from which it derives. Two requests against identical evidence under the same rule set return identical assessments.
primary_hypothesis identifies the best-supported explanation on record. It is not a verdict. alternative_hypotheses carries the remaining hypotheses with their scores, untested_hypotheses those on which no evidence bears, andmissing_evidence those evidence types not present, each with its status and the hypotheses it would discriminate between.
current_statuscases:readThe case status from which the assessment was derived.confidencecases:readOne of none, low, moderate, high or confirmed.primary_hypothesiscases:readThe leading explanation, with its score, support and contradiction counts, independent observation count, review rank and rule flags. Null where the case is not assessable.conclusioncases:readA single sentence stating the leading hypothesis, the evidence for and against it, the hypotheses not excluded, the case status and the applicable disclaimer.confidence_basis, contestedledger:readThe counts from which the confidence was derived. contested is set where two hypotheses tie, or where the rule set, the weighed evidence and the reviews do not agree.alternative_hypotheses, untested_hypothesesledger:readHypotheses addressed by the evidence or by a review, ranked; and those on which no evidence bears.supporting_evidence, contradicting_evidence, neutral_evidenceledger:readRelevance entries carrying ref, hypothesis, effect, reason and source_class. Each reason cites the fields and thresholds applied.missing_evidenceledger:readEvidence not present, each with a status of absent, pending, failed or withheld, and the hypotheses it would discriminate between.contradictionsledger:readMaterial contradictions: evidence against the leading hypothesis, disagreement between the rule set and the weighed evidence, a second review that does not concur, or conflicts between image and data recorded by a reviewer.limitationsledger:readStatements the assessment does not support, with the reason in each case.historyledger:readOne dated entry per recorded event, oldest first. Entries that change the status carry from_status, to_status and changed.based_onledger:readInput counts and the most recent input timestamp, permitting two assessments to be compared.attributionattribution:readVessel-source evidence and counts, rule flags, and review hypotheses at mechanism level with the evidence for and against each.source_class identifies the kind of input an entry represents. A hypothesis score is the count of its supporting entries less the count of its contradicting entries. Detector outputs are retained for traceability but do not contribute to hypothesis scoring.
detectorobservationinterpretationadjudicationThe score is an unweighted count. Entries are not scaled by quality grade, by the mutual independence of their sources, or by the strength with which they bear on the hypothesis. Quality determines which record is evaluated rather than how heavily it counts: the most recent record of each evidence type is used, with a grade of B or better preferred for cross-detection and optical coverage. Each record carries its own grade from A to D, so a consumer requiring weighted evidence can apply its own scheme to the same records.
Because the assessment is derived rather than stored, any change-feed entry for a case indicates that its assessment may differ from the one previously retrieved. Retrieve the case again, compare confidence, primary_hypothesis andcontradictions, and consult history for the event responsible. A human verdict closes a case and sets confidence to confirmed; evidence recorded before the verdict is retained unchanged.
confidence is a categorical value derived from the evidence. It is neither a score nor a probability. Only inputs beyond the detector contribute to it.
confirmedA human verdict is recorded.highTwo or more supporting inputs beyond the detector, at least one of them an observation, with nothing contradicting.moderateAt least one independent observation supports the leading hypothesis, and support exceeds contradiction.lowThe case is contested; or no input beyond the detector supports the leading hypothesis; or it is contradicted as often as it is supported; or it is supported only by an interpretation.noneNot assessable: context is not yet recorded, no wind value falls within two hours of acquisition, or wind lies outside the 2.5 to 15 m/s radar observability band. primary_hypothesis is null and the conclusion states the reason.Confidence counts inputs without grading them. Two C-grade observations satisfyhigh on the same terms as two A-grade observations, and no level distinguishes a strong independent detection from a weak one. The quality grade is present on each evidence record and confidence_basis reports the counts, so a stricter threshold can be applied downstream from the same data.
contested is set wherever the rule set, the weighed evidence and the reviews do not agree, and the disagreement is itself recorded under contradictions. A case identified by the rule set as a likely look-alike and also detected by an independent provider is contested; it is not resolved in either direction.
Hypotheses describe the physical nature of an observed feature. They stop short of source attribution: the nature of a feature is addressed under ledger:read, while the vessel or installation responsible for it is a separate matter addressed only under attribution:read.
s1Radar oil casesOIL_FILMMineral oil film, source unspecified. This is the hypothesis asserted by the detector.BIOGENIC_FILMNatural surfactant film or bloom.METEOROLOGICALLow wind, wind shadow or rain cell.OCEANOGRAPHICCurrent front, internal wave or shallow-water signature.SHIP_WAKEVessel wake or turbulence.ARTIFACTProcessing or scene-edge artefact.s2Optical material casesFLOATING_MATERIALDrifting material of the detected class. This is the hypothesis asserted by the detector.BENTHIC_OR_INTERTIDALSeabed vegetation or intertidal signature.CLOUD_OR_HAZECloud shadow or haze.FOAM_OR_WHITECAPFoam or whitecaps.FIXED_FEATUREA fixed feature: shoal, structure or moored object.VESSEL_OR_WAKEA vessel or wake within the scene.Independent reviews rank mechanisms drawn from a fixed and more specific vocabulary. Under ledger:read each mechanism maps to the hypothesis above it, so a review contributes its ranking without its source-level reasoning; underattribution:read the mechanisms are returned directly. That mapping, together with every reason code, confidence level, effect, status and evidence type used in responses, is published at /v1/vocabulary.
Each case carries a stack identifying the sensor it originates from. Both share a single response shape. The stack parameter on the cases endpoint filters between them, and the change feed carries both.
s1Sentinel-1 radar oildetector_score and p_oil_corpus, evaluated under the look-alike rule set, and adjudicated as CONFIRMED_OIL orCONFIRMED_NON_OIL.s2Sentinel-2 optical materialmaterial_class of macroalgae, floating debris or organic material, and a material_score in place of the oil scores. Evaluated under a separate material rule set, carries the optical disclaimer, and is adjudicated as CONFIRMED_MATERIAL or CONFIRMED_NON_MATERIAL.Optical cases accumulate the same context evidence: wind, sea state, static context, recurrence and optical coverage. Cross-detection against another provider, AIS evidence and the independent review track apply to radar cases only. Where a review is therefore absent, the assessment records it under missing_evidence.
The API is read-only. Every request carries a bearer key, issued per organisation.GET /v1/me returns the scopes a key holds, together with its plan, rate limit and monthly quota.
Scopes fall into three families, licensed separately. Within a family each scope includes the one before it; between families nothing is implied, so a key holding case scopes does not reach field reports or training labels.
A key cannot obtain material above its scopes through any endpoint, parameter or derived field. Where such material exists on a case, it is reported undermissing_evidence with a status of withheld.
Authorization: Bearer eysk_live_...cases:readCase snapshotledger:readEvidence ledgerattribution:readSource attributionreports:readField reportsreports:annotationsReport annotationslabels:readTraining labelspublicNo key requiredGET/v1/openapi.json
This document
GET/v1/vocabulary
Hypotheses, review mechanisms, reason codes, triage bands and signals, decision sources, rule versions, review roles, confidence levels, effects, statuses, evidence types, history kinds
200the vocabulary the assessment usescases:readCase snapshotGET/v1/me
The calling key: partner, plan, effective scopes, rate limit and monthly quota (any active key)
200partner, key {id, label, expires_at}, plan {id, name}, scopes[], tiers {cases, reports}, rate_per_min, quota {limit, used, remaining, resets_at}GET/v1/health
Liveness, ledger counts and freshness (any active key)
200ok, version, assessment_version, preview_signing, cases_total, cases_updated_24h, evidence_total, decisions_total, reviews_total, last_case_update, ledger_watermark, detections_replicated_to, detections_total, ledger_lag_s, replication_lag_s, last_case_update_age_s, reports_total, reports_recovered, reports_updated_24h, reports_replicated_toGET/v1/cases
Cases, newest-updated first, keyset-paged; each carries the assessment headline (full with expand=assessment)
sincequeryISO 8601; rows at or after this instantstatusqueryone status or a comma liststackquerys1 | s2s1 radar oil cases, s2 optical material cases; both when omittedbandqueryattribution:read. One triage band or a comma list of INVESTIGATE, WATCH and LOOKALIKE_SIGNALS; radar cases whose latest triage decision holds that band.bboxquerylon0,lat0,lon1,lat1 in EPSG:4326; order-insensitivelimitqueryPage size. Maximum 200, or 100 when expand is set.cursorqueryopaque next_cursor from the previous pageexpandquerycomma list of evidence,decisions,reviews,assessment (needs ledger:read; caps limit at 100). assessment = the full assessment per case instead of the headline200Page + cases[]GET/v1/cases/{case_id}/preview
Preview PNG (302 to a signed URL valid 1 h; ?redirect=false returns {url, expires_at})
case_idpath302redirect to the file404no preview503signing not configuredledger:readEvidence ledgerGET/v1/cases/{case_id}
One case; the full assessment at ledger:read and above, the headline at cases:read
case_idpathexpandquerycomma list of evidence,decisions,reviews,assessment (needs ledger:read; caps limit at 100). assessment = the full assessment per case instead of the headline200404GET/v1/cases/{case_id}/assessment
The assessment alone
case_idpath200GET/v1/cases/{case_id}/evidence
Full evidence chain with provenance and per-row relevance
case_idpath200GET/v1/cases/{case_id}/decisions
Decision trail as transitions, oldest first
case_idpath200GET/v1/cases/{case_id}/reviews
Independent reviews
case_idpath200GET/v1/changes
Change feed, oldest first: cases, evidence, decisions and reviews with ledger:read; report entries with reports:read.
sincequeryISO 8601; rows at or after this instantcursorqueryopaque next_cursor from the previous pagelimitqueryPage size. Maximum 1000.kindsquerycomma list of case,evidence,decision,review,report; without it the feed carries every kind the key is entitled to200reports:readField reportsGET/v1/reports
In-situ reports, newest-updated first, keyset-paged
sincequeryISO 8601; rows at or after this instantstatusqueryreported | recoveredscenequerycomma list of beach,nature,urban,at_seabboxquerylon0,lat0,lon1,lat1 in EPSG:4326; order-insensitivelimitqueryPage size. Maximum 200, or 100 with reports:annotations.cursorqueryopaque next_cursor from the previous page200Page + reports[]GET/v1/reports/{report_id}
One report
report_idpath200404labels:readTraining labelsGET/v1/labels
Detection-scoped verified labels with provenance
sincequerychannelqueryground_truth_link | app_confirmation | community_reviewtraining_eligiblequerycursorquerylimitqueryGET/v1/image-labels
Report-image annotations with provenance
sourcequeryreviewedquerytraining_eligiblequerycursorquerylimitqueryGET/v1/images/{id}
Report image (302 to the file; labels:read)
idpathredirectqueryShared across endpoints.
sincequerydate-time/v1/cases, /v1/changes, /v1/reportslimitqueryinteger/v1/caseslimitqueryinteger/v1/changescursorquerystring/v1/cases, /v1/changes, /v1/reportsexpandquerystring/v1/cases, /v1/cases/{case_id}bboxquerystring/v1/cases, /v1/reportscase_idpathuuidreport_idpathuuidlimitqueryinteger/v1/reportsResponse fields, as published in the OpenAPI document.
Errorerrorstringrequired_scopestringScoress1 cases carry detector_score, decision_source, p_oil_corpus, champion_* and challenger_*; s2 cases carry material_score
material_scorenumber | nulls2 only: Stage 2 adjudicator output for the detected class; ranks, is not a probabilitydetector_scorenumber | nullThe score of the Stage 2 model named in decision_source. It ranks candidates and is not a probability.decision_sourcestring | nulls1 only. The Stage 2 model whose score decided: challenger is the fine tuned model, champion the frozen model, champion_large a candidate of 10 km² or more that the frozen model scored higher. Dates at /v1/vocabulary under decision_sources.champion_scorenumber | nulls1 only. The frozen Stage 2 model's score for the same candidate.champion_spreadnumber | nullp_oil_corpusnumber | nullIsotonic calibration of detector_score for the deciding model, fitted on scenes held out of training. Valid in corpus and before adjudication.challenger_scorenumber | nulls1 only. The fine tuned Stage 2 model's score for the same candidate.challenger_spreadnumber | nullensemble_spreadnumber | nullnotestringRefwhat an assessment entry points at; every claim is traceable to a ledger row or to the detection
kindstringiduuidnull at cases:read (the snapshot carries no row ids) and for the verdicttypestringevidence type, for evidence refsRelevancewhat one input means for one hypothesis
refanyhypothesisstringeffectstringreasonstringcites the fields and thresholds usedsource_classstringDetector outputs are retained for traceability but do not contribute to hypothesis scoring.EvidenceRelevancehypothesisstringeffectstringreasonstringsource_classstringPresent when the row is not an observation: MEMORY rows are interpretation, GROUND_TRUTH rows adjudication.Hypothesisa nature-level explanation of the observed feature with its score (support − contradiction, detector excluded)
hypothesisstringlabelstringdetector_originbooleantrue for the family the detector itself claims (OIL_FILM, FLOATING_MATERIAL)scoreintegersupportintegercontradictionintegerindependent_observationsintegerreview_rankinteger | nullwhere the latest independent review ranked this family, if it didrule_flagsarrayreason codes of the latest rule decision that speak for this familyMissingEvidenceevidencestringstatusstringreasonstringwould_discriminatearrayContradictionevery material contradiction stays visible
kindstringdetailstringagainststringrefanyitemsarrayattribution:read: the reviewer's own conflict statementsHistoryEntryone dated event; status-moving entries carry from_status / to_status / changed so every transition is reconstructable
atdate-timekindstringfrom_statusstring | nullto_statusstringchangedbooleanrefanysummarystringtypestringqualitystringeffectsarrayrule_versionstringreason_codesarrayreason_meaningsarraybandstringattribution:read: the band of a triage decisiontriage_signalsarraythe triage signals that hold, filtered by scoperolestringmodelstringtop_hypothesisstring | nulloptical_readingstring | nullconcursboolean | nullAssessmentDerived, versioned, deterministic. cases:read receives the headline (assessment_version, assessed_at, current_status, confidence, primary_hypothesis {hypothesis, label, support, contradiction}, conclusion); ledger:read receives everything; attribution:read adds `attribution`.
assessment_versionstringassessed_atdate-timecurrent_statusstringconfidencestringcategorical and rule-derived, never a probability; see /v1/vocabularyconfidence_basisstringcontestedbooleana tie between hypotheses, or the tracks (rules, review, second reading) disagreebased_onobjectprimary_hypothesisanynull when not assessablealternative_hypothesesarrayevery family the evidence or a review touched, best firstuntested_hypothesesarrayfamilies nothing on file bears onsupporting_evidencearraycontradicting_evidencearrayneutral_evidencearraymissing_evidencearraycontradictionsarrayconclusionstringbounded: names the primary hypothesis, what supports and contradicts it, what is not excluded, the case status, and the disclaimerlimitationsarrayhistoryarrayattributionobjectattribution:read only: vessel_source {support, contradiction, evidence[]}, rule_flags, source_hypotheses (mechanism-level, ranked, with evidence_for / evidence_against), second_reading, reviewer_conflicts, reviewer_missing, triage {band, rule_version, decision_id, decided_at, signals}CaseWith cases:read: identifiers, timestamps, status, position, scores, the newest context per evidence type, rule_flags, verdict, preview and the assessment headline. With ledger:read: gate_reason, status_reason, geometry, detector_features, evidence_counts, decision_count, review_count, the full assessment (on the single-case route, or with expand=assessment) and any expanded arrays. With attribution:read: triage, the latest triage decision of a radar case. stack s1 is a Sentinel-1 radar oil case; s2 is a Sentinel-2 optical material case with material_class set and material_score in scores.
iduuiddetection_iduuidscene_idstringstackstringmaterial_classstring | nulls2 only: macroalgae, floating_debris, organic_materialobserved_atdate-timeingested_atdate-timeupdated_atdate-timestatusstringarea_m2number | nulllonnumberDecimal degrees; 3 decimals with cases:read, 5 with ledger:read.latnumberscoresanywindobject | nullwavesobject | nullcontextobject | nullrecurrenceobject | nullcross_detectionobject | nulloptical_coverageobject | nullverdictobject | nullrule_flagsarrayReason codes that fired in the latest rule decision, filtered by scope. Meanings are listed in /v1/vocabulary under reason_codes.triageobject | nullattribution:read. The latest triage decision of a radar case: band, rule_version, decision_id and decided_at. Band meanings are listed in /v1/vocabulary under triage.assessmentanypreviewobjectdisclaimerstringgate_reasonstring | nullstatus_reasonstring | nulloperator text; attribution segments removed below attribution:readgeometryobject | nulldetector_featuresobject | nullevidence_countsobjectdecision_countintegerreview_countintegerevidencearraydecisionsarrayreviewsarrayEvidenceiduuidtypestringWIND, WAVES, STATIC_CONTEXT, RECURRENCE, CROSS_DETECTION, OPTICAL_COVERAGE, FIELD_REPORT, MEMORY, GROUND_TRUTH; AIS, AIS_EVENTS and AIS_TRACK at attribution:readsourcestringsource_uristring | nullqualitystringQuality grade A to D, assigned per source. C marks a provisional row that is re-checked; D marks a source that was queried and failed. Only rows graded A or B can support or contradict a hypothesis; C and D rows are recorded as neutral.observed_atdate-timeretrieved_atdate-timefactsobjectrelevancearraywhat this row means for each hypothesis it bears onDecisionone rule-set evaluation or human verdict, as a transition
iduuidstatusstringrule_versionstringlookalike-rules-0.x and triage-0.x (s1), material-rules-0.x (s2), human-verdict-1reasonsobject | nullFlag to boolean; the flags that fired are listed in reason_codes. Vessel flags at attribution:read. A triage decision carries the lookalike flags it read and a triage object of signals; its band and the vessel derived signals are served at attribution:read. Human verdicts: {human_verdict: true, note}.inputsobject | nullthe exact facts the rule set read: static_context, recurrence, wind, detector_features, area, score, axis_azimuth_deg, wind_alignment_deg; human verdicts: {prior_status, detector_score}from_statusstring | nullchangedboolean | nullreason_codesarrayreason_meaningsarraysummarystring | nullcreated_atdate-timeReviewAn independent review of the case. Reviews do not change case status.
iduuidrolestringmodelstringprompt_versionstringconcursboolean | nulloptical_second only. Derived by comparison rather than reported by the reviewer: true when the top-ranked mechanism equals that of the primary review.optical_readingstring | nullprimary_review_idstring | nullcreated_atdate-timehypothesis_familiesarraythe ranking as nature-level families (ledger:read)conflict_countintegermissing_countintegerhypothesesarrayattribution:read: ranked mechanisms with evidence_for[] and evidence_against[]image_notesstringoptical_notesstringmissingarrayattribution:read: discriminators the reviewer asked forconflictsarrayattribution:read: contradictions the reviewer flagged between image and datacomparisonobjectChangekindstringiduuidcase_iduuidnull for report entriesatdate-timedetailobjectcase: {status, stack}; evidence: {type, quality, source}; decision: {status, rule_version, from_status, changed, reason_codes}, with band on a triage decision at attribution:read; review: {role, model, prompt_version, top_hypothesis (family at ledger:read, mechanism at attribution:read), concurs}; report: {status, scene, country}ReportOne in-situ report. reports:read fields always; reports:annotations adds annotations and analysis and serves the position at 5 decimals.
iduuidreported_atdate-timestatusstringrecovered_atdate-timerecovery_sourcestring | nulllegacy_flag: the previous platform marked the report recovered without a datesourcestringlonnumber3 decimals with reports:read, 5 with reports:annotationslatnumbercountrystring | nullcitystring | nullprotected_areaobject | nulleventobject | nullthe clean-up event the report belongs toscenestring | nullscene class from the image modelimpactstring | nullseverityinteger | null1 to 5primary_typestring | nullthe reporter's or model's main class; see /v1/vocabulary item_classesitemsobject | nullitem class → count, from the image modelcategoriesobject | nullcoarse category → countmaterialsarray | nullmaterials the reporter selectedweight_kgnumber | nullreported weight; null when only the placeholder value was recordedphotosarrayupdated_atdate-timedisclaimerstringannotationsarrayreports:annotations: per-image object detections {class, bbox [x, y, w, h] normalised, confidence} with source and model_versionanalysisobject | nullreports:annotations: the model's scene labels, detected scene, pollution type counts, detected objects, people count, confidence and descriptionPagegenerated_atstringcountintegerlimitintegernext_cursorstring | nullLoad the ledger once, then follow the change feed. Page the cases endpoint with the expansions required until next_cursor returns null.
GET /v1/cases?expand=evidence,decisions,reviews,assessment&limit=100Then begin the change feed from a point several minutes before the load started, follownext_cursor to the end, and poll that final cursor to remain current. Overlap the initial since and deduplicate by id: entries are ordered by their own timestamp, and an entry committed late may carry a timestamp earlier than a cursor taken in the interim.
GET /v1/changes?since=2026-09-08T00:00:00Z&limit=1000Retrieve a case again on any change entry referring to it. Each entry identifies the record that changed, permitting exact retrieval and deduplication, and carries detail specific to its kind: a decision entry reports the status produced and the status it replaced, so a status change is visible in the feed itself.
No freshness bound is guaranteed. Where the processing node falls behind, responses continue to be served from the evidence on record, the change feed becomes quiet, and items marked pending under missing_evidence do not advance. Detections continue to accumulate upstream and are processed in order once the node resumes. ledger_lag_s in the health response reports the age of the most recent detection ingested: values in minutes are expected, values in hours indicate that the node is behind.
400401403404429503Scores are model outputs. They rank candidates and do not measure or confirm the presence of oil, floating material or any material type, and no score identifies a source. Every response describing a detection carries a disclaimer field stating the applicable limitation for its sensor. Confidence is derived separately and is not a score.
detector_scorep_oil_corpusmaterial_scorechallenger_scoreensemble_spreadAdditive changes do not alter the version in the path. Removals and renames would.
The OpenAPI 3.1 document describes every endpoint on this page and imports into Postman, Insomnia or any client generator. It requires no key. The base URL is not included in the document and is issued with the key.
curl -H "Authorization: Bearer $KEY" "$BASE/v1/health"$BASE is issued with the key. The health endpoint returns the assessment rule version, ledger counts, replication watermarks and ledger_lag_s, and is the most direct way to confirm that a key is active.