# EU ETS Voyage Carbon Exposure Evidence (`zinin/voyage-carbon-compliance-evidence`) Actor

Calculate deterministic EU ETS voyage exposure from buyer-supplied vessel, route, and fuel facts using pinned official fixtures. Get gas, fuel, segment, truth-ledger, confidence, limitation, and receipt evidence without provider calls or an automatic legal decision.

- **URL**: https://apify.com/zinin/voyage-carbon-compliance-evidence.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:** MCP servers, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.085 / delivered voyage exposure report

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## EU ETS Voyage Carbon Exposure Evidence

Calculate a deterministic estimate of EU ETS voyage exposure from facts you already own: vessel class and GT, ordered port calls, fuel quantities, optional allowance price, and an optional mass scenario. The result is an evidence ledger with official fixture references, assumptions, limitations, and reproducible digests.

This is derived evidence, not a compliance verdict, MRV verification, legal advice, allowance instruction, or regulatory assurance. It does not decide whether a company is legally liable. Vessel, port, quantity, and price facts remain buyer supplied and unverified.

![EU ETS Voyage Carbon Exposure Evidence: buyer facts to bounded calculation](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/b62975b61b6f203aab72ce806d5303cae8844922/commercial115/voyage-carbon-compliance-evidence/readme-hero.webp)

![EU ETS Voyage Carbon Exposure Evidence: evidence and review workflow](https://raw.githubusercontent.com/TimmyZinin/apify-actor-assets/b62975b61b6f203aab72ce806d5303cae8844922/commercial115/voyage-carbon-compliance-evidence/readme-workflow.webp)

### What goes in and what comes out

You provide one closed `decision` object with `schemaVersion`, `requestId`, `reportingYear`, vessel, two to nine ordered port calls, one to eight legs, fuel records, optional berth fuel records, optional buyer price, and optional scenario. Decimal quantities are strings such as `100` or `12.345`; `1.0`, exponent notation, duplicate JSON keys, non-ASCII text, unknown keys, future facts, cross-year intervals, unsupported fuels, and unmodelled legal branches are rejected.

The Actor returns one closed terminal `OUTPUT` record and one receipt-free prepared Dataset evidence row for a complete decision. The Dataset row is written before the pinned SDK charging operation and carries `preparedReportDigest`; it never claims that payment or delivery was confirmed. `OUTPUT` contains the authoritative delivery receipt, exposure totals, gas/fuel/segment breakdowns, the full truth ledger, data-quality and confidence fields, eight base limitations, evidence references and SHA-256 digests. The authoritative ledger is one `truthLedger.entries` array, with each entry bound to one truth class and a global maximum of 128 entries across classes. `verifiedInputs` is always exactly `[]` with `verificationReason="mrv_verification_not_performed"`; redundant dynamic count claims are absent from public output and Dataset rows. Absent optional `price` and `scenario` inputs are represented by explicit `missing` entries. Decision and prepared rows contain exactly the seven accepted source identities in registry/runtime order; non-decision rows contain no sources. It never stores raw IMO numbers, UN/LOCODEs, port or leg IDs, buyer timestamps, masses, prices, source HTML, credentials, or buyer text.

Every terminal timestamp is canonicalized to strict UTC seconds. After `Actor.init`, the production run records the platform start event as exactly count `1`, obtains the charging-manager pricing context before storage access, and carries that immutable context into fallback terminals. Unknown tier or price fields remain `null` under a fail-closed reason; they are never reported as zero. Direct library and MCP execution is explicitly off-platform and fail-closed: it cannot claim a platform run, has a null start count and `off_platform_no_platform_run` reason, and performs no paid operation. One injected 90-second deadline covers the default KVS input/output reads, counters, and delivery; terminal `OUTPUT` writing has its separate bound. The production runtime has no named persistence, request-queue lock, external service, or cross-run deduplication. Each run is one independent voyage report. `stateReceipt.status=not_attempted` and `reason=single_voyage_no_persisted_state` are truthful: raw buyer input is never persisted.

### Exact examples

The public Task input is byte-exactly [`fixtures/public-task-input.json`](fixtures/public-task-input.json). The decision input is [`examples/decision.json`](examples/decision.json). The following files are generated by the runtime with the fixed observed time `2026-08-09T20:00:00Z`, not hand-authored fixtures:

- [`examples/demo-output.json`](examples/demo-output.json) is the complete terminal `OUTPUT` for the public Task.
- [`examples/demo-dataset-row.json`](examples/demo-dataset-row.json) is its receipt-free demo Dataset row.
- [`examples/decision-output.json`](examples/decision-output.json) is the complete paid decision `OUTPUT`, including the authoritative receipt.
- [`examples/decision-dataset-row.json`](examples/decision-dataset-row.json) is the prepared, receipt-free Dataset row pushed before charging.

These files are the exact JSON examples used for contract review. They contain no raw buyer timestamp or price date: `effectiveAt` and `monetaryExposure.priceDate` are always `null`, with `private_input_redacted` or `priceTruth` explaining the projection.

### Rule boundary

V1 accepts cargo and passenger ships of at least 5,000 GT for reporting years 2024 through 2027. It models HFO, LFO and MDO/MGO with pinned official factors, CO2/CH4/N2O GWPs, route shares and phase-in. Offshore, general cargo, excluded classes, Article 12 treatments, special-port treatments, LNG/Bio-LNG and every other unsupported fuel fail closed.

The calculation uses exact local official fixtures captured from EUR-Lex, the Publications Office and the European Commission. Runtime makes zero provider or network calls. Sources are cited in the output by canonical URL, fixture path, digest, capture time, evidence-manifest digest and attribution. Raw source bytes are never emitted. Source rights are evidence and attribution controls, not a legal guarantee.

The normative `ruleSetDigest` uses the established evidence-manifest registry order, not lexical sorting. The accepted golden vectors are pinned for reporting years 2024, 2025, 2026 and 2027 in the runtime constants.

### Pricing

Pay per event: one `apify-actor-start` event plus one `voyage-report-found` event for one complete decision. With pinned `apify@3.7.2`, a Dataset push with an event also records one implicit `apify-default-dataset-item` counter; that event is configured at USD 0.000000. The exact six Apify account tiers are FREE, BRONZE, SILVER, GOLD, PLATINUM and DIAMOND. The run cap is checked before the single paid push. A synthetic demo writes one unpriced row and emits zero result events. The confirmed decision records one implicit default-Dataset event and verifies `defaultDatasetDeltaEqualsCount=true`; response loss, charge failure, or counter mismatch produces nullable ambiguous billing and no retry. A direct rerun is a new run and may produce a new result charge; the Actor makes no cross-run idempotency claim.

Direct Hub reference price is USD 0.15. No external provider spend is required. This product does not renew or use Datalastic.

### Machine use and MCP

The package exports `voyage_carbon_compliance_evidence.run` through `./mcp`. MCP calls are an off-platform boundary: the literal output is a truthful fail-closed terminal with `lifecycle="off_platform"`, no platform start count or price, and `OFF_PLATFORM_EXECUTION`; it cannot claim `apify-actor-start` or charge an Apify run. `contract/mcp.json` embeds literal `inputSchema` and `outputSchema` objects, so callers do not depend on path-only schema references. The adapter is deterministic and suitable for n8n, Make or another machine caller; the caller must supply buyer-authorized facts and must not send credentials.

The contract/runtime/MCP schemas are authoritative. Apify's UI schema is a deliberately permissive input projection because its editor rejects JSON Schema nullable branches and cross-field `oneOf` constraints; runtime validation remains fail-closed and enforces the exact two-mode contract.

### Fixed synthetic Task

Use the exact object in `fixtures/public-task-input.json`. It performs no source/provider call, uses no prior state, writes one unpriced demo row, and proves zero `voyage-report-found` events.

### Production runtime amendment R7

The accepted production path is intentionally stateless for this independent-voyage product. It opens only the default per-run KVS for `INPUT` and `OUTPUT`, and uses the default Dataset through the pinned SDK. It does not open a named KVS, create a request queue, call a provider, or use an external persistence service. No caller-carried continuation state is required for the current contract. A result charge is attempted once, only after affordability and all output limits pass; the runtime reads counters after the single delivery attempt and never retries inside the run. If delivery or counter confirmation is ambiguous, the terminal result is `delivery_unknown` with nullable result counters. This is not a successful report and is not charged as a confirmed result.

### Limitations

- This is not legal advice, MRV verification, a compliance conclusion, or a surrender instruction.
- Source facts are pinned at the accepted-through-2027 boundary and require a new review before extension.
- Buyer facts are not independently verified; estimated masses and scenarios are explicitly marked.
- The output is not a market allowance quote and does not calculate company-wide aggregation, verifier findings, penalties or transfer pricing.

### Related Actors

| Actor | Why it is related |
| --- | --- |
| [Strait of Hormuz Tanker Flow Recovery](https://apify.com/zinin/strait-of-hormuz-tanker-flow-recovery) | Regional maritime flow context. It is not a source for this calculation. |
| [Singapore Bunker Tanker Divergence](https://apify.com/zinin/singapore-bunker-tanker-divergence) | Separate Singapore bunker signal for a workflow context layer. |
| [Panama Canal Queue Transit Imbalance](https://apify.com/zinin/panama-canal-queue-transit-imbalance) | Separate transit signal; it does not provide vessel compliance facts. |
| [Container Exception Evidence](https://apify.com/zinin/container-exception-evidence) | Customer-authorized container evidence workflow; no data is fetched between Actors. |

### Support contract

Every terminal status is closed and machine-readable. An unknown fact is null with a closed reason. A source, rights, state, delivery, billing or schema problem prevents a decision rather than producing a guessed number.

### What you get

The paid unit is one complete, deterministic voyage-exposure report. A complete report gives an operations or compliance reviewer a bounded calculation package rather than a loose number:

- one stable request digest and one prepared-report digest;
- the accepted reporting year and pinned rule version;
- all-gas calculated CO2e, ETS-eligible CO2e before route scope, covered CO2e, and phase-in-adjusted allowance equivalent;
- gas-level rows for CO2, CH4, and N2O;
- fuel-level and voyage-segment breakdowns;
- an optional buyer-supplied allowance-price scenario, clearly separated from the regulatory calculation;
- an assumption ledger for estimated quantities and scenarios;
- a truth ledger that classifies every mapped input or rule fact as buyer reported, measured as submitted, estimated, official-rule fixture, contract control, or missing;
- seven ordered official-source references with canonical URLs, fixture hashes, evidence-manifest hashes, capture dates, and attribution;
- data-quality and confidence objects with explicit reasons;
- a closed limitations list;
- one receipt-free Dataset row for analysis;
- one authoritative KVS `OUTPUT` record for terminal, delivery, and billing reconciliation.

The Actor does not emit a regulatory approval flag. `result.decision.decisionType="calculated_exposure"` means the accepted arithmetic was performed under the pinned model. It does not mean the vessel, company, route, quantities, port classifications, exemptions, or legal obligations were independently verified.

Free and non-result outcomes remain useful. The synthetic public Task writes one free demonstration row. Invalid input, unsupported branches, stale source review, pricing mismatch, budget stop, and off-platform execution return a closed terminal record when the storage boundary remains available. They do not intentionally emit `voyage-report-found`.

### Who uses it

#### Maritime compliance analysts

Use the report as a reproducible worksheet before a qualified person checks vessel scope, port-call treatment, fuel evidence, reporting-year rules, verifier expectations, and company-level aggregation. The Actor reduces arithmetic and evidence-linking work; it does not replace the analyst.

#### Shipping and logistics finance teams

Use the allowance-equivalent quantity with a separately supplied planning price to create an internal exposure scenario. The price is buyer asserted, not a live quote. The output is not an invoice, accrual policy, transfer-pricing instruction, or financial statement.

#### Data engineering teams

Use the closed schemas, deterministic digests, exact decimal rules, and stable source order to test upstream voyage-data pipelines. The report makes missing, rejected, estimated, and buyer-reported facts visible instead of silently coercing them.

#### Governance and assurance teams

Use source hashes, attribution, accepted-through-year controls, and the truth ledger to review what the calculation relied on. Retain the input under your own policy; the public report deliberately redacts raw vessel, port, timestamp, mass, and price values.

#### Product and workflow builders

Use Dataset for receipt-free evidence display and KVS `OUTPUT` for terminal automation. Route all positive reports to human review. Do not trigger allowance purchase, regulatory filing, customer representation, penalty response, or vessel-operating decisions directly from this Actor.

### How to run

Start with the fixed synthetic Task if you are evaluating the product. It proves the schema, output shape, free Dataset path, and zero-result billing path without using private voyage data.

For a real calculation, submit one `decision` object. A minimal production workflow is:

1. choose a supported reporting year from 2024 through 2027;
2. create a random request ID matching `rq_[0-9a-f]{32}`;
3. supply a supported vessel branch and gross tonnage;
4. order two to nine port calls chronologically;
5. connect those calls with one to eight legs;
6. provide bounded HFO, LFO, or MDO/MGO mass records with a declared truth class;
7. optionally provide berth quantities, a planning allowance price, or one mass-multiplier scenario;
8. confirm the closed rights and unfetched-reference attestations required by the input schema;
9. set a run charge cap that covers the automatic start event and one result event at your current Apify tier;
10. after terminal completion, read KVS `OUTPUT` first and Dataset second.

API pattern:

```bash
curl -X POST "https://api.apify.com/v2/acts/zinin~voyage-carbon-compliance-evidence/runs?token=$APIFY_TOKEN" \
  -H 'content-type: application/json' \
  --data-binary @voyage-input.json
```

Poll the returned run ID to a terminal platform state. Do not start another run merely because Dataset is slow to appear. If `OUTPUT.status="delivery_unknown"`, preserve the run, Dataset, KVS, logs, and platform charge counters for manual reconciliation; do not blind-retry.

The Actor is intentionally one-voyage-per-run. Split multiple voyages into separately capped and reconciled runs. This keeps source, input, result, billing, and reviewer evidence attributable to one calculation.

### Input contract

The normative contract is [`contract/input.schema.json`](contract/input.schema.json). The Apify Input UI is a compatible projection; runtime validation is authoritative where the UI schema cannot express cross-field rules.

#### Identity and time

- `schemaVersion` is exactly `1.1.0`.
- `mode` is `decision` for a paid calculation or the fixed synthetic mode for the public Task.
- `requestId` is caller generated and bounded. It is not a vessel identifier.
- all timestamps are strict UTC-second strings;
- buyer fact timestamps may not be in the future relative to the run's observed time;
- intervals may not cross the accepted reporting-year boundary;
- duplicate JSON keys, non-ASCII payload bytes, BOMs, unknown fields, and exponent-number shortcuts fail closed.

#### Vessel branch

The current model is deliberately narrow. It accepts only branches represented by the pinned V1 rule set. Unsupported vessel categories, excluded classes, Article 12 treatments, general-cargo branches, and sub-5,000-GT scope fail before the paid push. A rejection is not a legal conclusion that the voyage is outside EU ETS; it means this model will not calculate that branch.

#### Port calls and legs

Port calls must be chronologically ordered and legs must connect adjacent accepted calls without overlap. `memberStatePort` and `isPortOfCall` are buyer assertions. The Actor does not geocode a UN/LOCODE, verify a port call, determine evasive routing, or resolve neighbouring-transhipment treatment. Any declared special treatment is rejected because the V1 model does not classify it.

#### Fuel quantities

Each quantity has a stable record ID, a closed fuel type, interval, decimal mass string, and `dataTruth`. `measured` means measured as submitted, not independently MRV-verified. `buyer_reported` and `estimated` remain distinct. HFO, LFO, and MDO/MGO are supported; LNG, bio-LNG, LPG, methanol, biofuel, and other fuels fail closed until a separately reviewed formula is released.

#### Optional price and scenario

`priceInput` is a buyer-supplied planning value. Its date is redacted from persisted output. `scenario.alternativeFuelMassMultiplier` is an arithmetic what-if input, not an engineering, safety, procurement, or legal recommendation.

#### Rights and privacy attestations

Submit only facts you control or are authorized to process. Do not submit credentials, charter-party secrets, commercially sensitive narrative, sanctions-sensitive instructions, crew information, personal data, or source-document bytes. The Actor stores digests and derived evidence, but Apify retains run input according to your workspace settings.

### Happy, partial, and failure output

#### Happy decision

For a complete supported request, the Actor creates one prepared Dataset row, attempts one linked `voyage-report-found` push, reads the named and implicit Dataset counters, and writes terminal `OUTPUT`. A confirmed report has:

- `status="decision"`;
- `resultFound=true` in terminal OUTPUT;
- one prepared Dataset row whose own `resultFound=false` remains settlement neutral;
- `deliveryReceipt.status="confirmed"`;
- one confirmed result event and one confirmed implicit Dataset operation;
- exact result and Dataset counter deltas of one;
- no persisted baseline or cross-run state;
- one closed truth ledger and seven ordered source rows.

#### Free synthetic demo

The fixed public Task creates one Dataset demo row and no result event. It is safe for schema exploration, but its numbers are synthetic and must never be used in operations, filings, purchasing, or customer reporting.

#### Rejected request

Schema, time, vessel, port, fuel, source, or representation problems produce a closed reason and error code. A rejection does not return a partial exposure number. Fix the input or review the unsupported branch; do not reinterpret nulls as zero.

#### Pricing or budget stop

An unknown pricing tier, malformed event map, mismatched price, unreadable cap, or other pricing inconsistency fails closed before delivery. A budget stop means the current cap could not safely cover the result event after the platform start event. Raise the cap only after reviewing the live pricing panel and current tier.

#### Delivery unknown

If the linked push throws or required counters are unavailable or inconsistent, the Actor does not retry. `OUTPUT` records an ambiguous terminal state when it can. Dataset delivery and payment must then be reconciled from the platform run, Dataset, KVS, and event counters. Never assume ambiguous means free, and never assume it means paid.

#### KVS output failure

An OUTPUT write/read-back failure is a hard runtime failure. The platform run status and retained Dataset/event evidence become the recovery surface. Do not create a second paid run until the first attempt has been reconciled.

The product intentionally has no "partial compliance decision" status. Unsupported or incomplete rule branches are rejected rather than calculated with guessed assumptions.

### Field dictionary

| Field group | Meaning | Boundary |
|---|---|---|
| `requestDigest` | Deterministic digest of the accepted normalized request | Does not make the request anonymous or prove ownership |
| `preparedReportDigest` | Digest of the receipt-free Dataset evidence projection | Excludes terminal delivery settlement |
| `inputIdentity` | Digested request, vessel, voyage, and contract identities | Raw private identifiers are not persisted in public output |
| `rule` | Reporting year, phase-in, gases, source and rule-set digests | Not a full legal applicability analysis |
| `result.decision.scopeTotals` | Calculated, eligible, covered, and allowance-equivalent totals | Deterministic model output, not verifier-certified data |
| `result.decision.byGas` | CO2, CH4, and N2O breakdown | Uses pinned V1 factors and accepted fuel branches only |
| `result.decision.byFuel` | Accepted quantity aggregation by fuel | Does not assess fuel sustainability or proof of origin |
| `result.decision.segments` | Route-scope calculation by digested segment | Buyer port classifications remain unverified |
| `allowanceExposure` | Phase-in-adjusted planning quantity | Not a surrender instruction or purchase recommendation |
| `monetaryExposure` | Optional buyer-price scenario | Not a live market quote or accounting conclusion |
| `scenarioComparison` | Optional mass-multiplier comparison | Not an operational fuel-switch recommendation |
| `assumptionLedger` | Every estimated quantity or scenario input | Does not validate the assumption |
| `truthLedger.entries` | Canonical per-fact truth classification | Array entries are authoritative; no redundant count claim |
| `truthLedger.verifiedInputs` | Independently verified buyer inputs | Always empty in V1 |
| `sources` | Seven ordered official-source identities and hashes | Source citation, not official endorsement |
| `dataQuality` | Completeness, truth mix, and mapping reasons | Contract completeness, not real-world accuracy |
| `confidence` | Deterministic confidence level and reasons | Not a legal or statistical probability |
| `limitations` | Closed unsupported and non-verification boundaries | Must travel with downstream use |
| `billing` | Tier, prices, counters, cap, and parity | Terminal OUTPUT only is authoritative |
| `dataset` | Prepared-row write facts | A Dataset row is not a payment receipt |
| `deliveryReceipt` | Expected and confirmed row/result counts | Ambiguous values remain null with reasons |
| `stateReceipt` | Stateless product declaration | V1 does not persist voyage history |
| `errors` | Closed code, field group, and digested detail | Never replace review of platform logs for an ambiguous run |

### Evidence and boundaries

#### Official fixtures

Runtime uses exact local fixture bytes captured from official EU sources and pinned by SHA-256. The source registry binds capture metadata, media type, authoritative and final URLs, clause evidence, an accepted-through-year limit, a current-law inventory, and an independent review artifact. Any missing, altered, stale, out-of-order, or unapproved fixture blocks a decision.

#### Attribution and reuse

Output cites the instrument or official page, canonical URL, legal version, and attribution required by the registry. It identifies the product as a derived calculation. It does not emit raw HTML, legal prose, page images, EU emblems, logos, or third-party works. Rights metadata supports release governance; it is not legal advice to the buyer.

#### Buyer facts

Vessel, voyage, port, quantity, price, and scenario facts come from the buyer. The Actor does not fetch AIS, carrier, terminal, MRV verifier, registry, fuel supplier, bunker note, invoice, charter, or allowance-market data. It cannot confirm completeness or detect omitted voyages and quantities.

#### Freshness

The accepted rule years are bounded through 2027 by the checked artifacts. A new reporting year, amendment, source change, policy change, legal interpretation, or fixture-capture change requires a new source and independent-review release. The runtime will not silently extend itself.

#### Privacy and confidentiality

Although persisted evidence is digested and redacted, run INPUT is workspace data. Treat vessel schedules, commercial routes, bunker quantities, prices, and contract references as confidential. Apply least privilege, retention limits, deletion controls, and purpose restrictions. Hashing enables linkage and is not anonymization.

#### Security

The production runtime performs zero provider requests and follows no buyer URL. Citation URLs are data, never fetch targets. There is no browser, proxy, DNS, webhook, email, request queue, or child Actor. This removes SSRF and provider-token exposure from the V1 runtime, but it does not replace workspace access control.

### Decision routing

| Terminal state | Human route | Automation boundary |
|---|---|---|
| `decision` | Review scope, truth ledger, sources, limitations, and settlement | May populate a review queue; may not file, buy, certify, or notify automatically |
| `demo` | Product evaluation only | Never use synthetic numbers operationally |
| `rejected` | Correct input or escalate an unsupported branch | Do not coerce nulls or remove validation |
| `build_hold` | Refresh and independently review pinned sources | No calculation or paid delivery |
| `pricing_misconfigured` | Compare exact live event map and tier | No paid push |
| `budget_stopped` | Review cap and price before a new run | Do not infer a result |
| `delivery_unknown` | Reconcile run, Dataset, KVS, and event counters | Never blind-retry |
| off-platform rejection | Invoke as an Apify run for platform settlement | MCP/library call cannot claim paid delivery |

Even a green `decision` is `safeToAutomate=false` in business terms. The machine-safe action is to store or route the evidence for review. All legal, filing, payment, procurement, customer, and vessel-operation actions require separately authorized human controls.

### Commercial playbooks

#### Pre-review voyage worksheet

Normalize one voyage, retain the request digest with your source packet, and assign a compliance analyst. Have the reviewer compare every buyer-reported or estimated truth entry with controlled source evidence before using the allowance equivalent.

#### Finance planning scenario

Supply an internal planning price and preserve its source outside the Actor. Treat `monetaryExposure` as a scenario. Reconcile it to company policy, currency treatment, allowance position, verifier data, and accounting review before booking or purchasing anything.

#### Data-pipeline acceptance

Run stable fixtures through staging, compare request and report digests, and monitor rejected field groups. Use rejection rates to improve upstream voyage-data quality. Do not weaken the runtime schema to make a noisy feed pass.

#### Rule-change impact review

When a source or accepted year changes, freeze the old build, capture new official bytes, update the rights and evidence manifests, conduct an independent review, generate a new rule-set digest, and compare outputs on controlled fixtures. Never patch a factor or phase value directly in production.

#### Audit packet

Retain the accepted input under access control, build ID, run ID, Dataset ID, KVS ID, terminal OUTPUT, report digest, source manifest, exact fixture revision, and reviewer sign-off. The Actor provides components of an evidence packet; it does not create regulatory sufficiency by itself.

### Integration recipes

#### Dataset consumer

Read the single prepared row only after the run is terminal. Verify `status="prepared"`, recompute or compare `preparedReportDigest`, require seven source rows in registry order, and retain limitations. Then fetch KVS `OUTPUT` to determine whether the prepared row settled as a confirmed result.

#### KVS-first orchestrator

Read `OUTPUT.status`, `resultFound`, `deliveryReceipt`, `billing.counterParity`, and `errors`. Continue only for a confirmed decision with expected counters. Route every other state to a manual queue. A missing OUTPUT is a hard stop, not an empty result.

#### Warehouse model

Store digests as strings, numeric decimal outputs as decimal strings, nested ledgers as JSON, and source rows in a child table if needed. Keep terminal billing facts separate from the receipt-free prepared row. Do not convert exact decimal strings through binary floating point before reconciliation.

#### Webhook

If your Apify integration emits terminal webhooks, use the webhook only as a signal to fetch current run storage. Do not place buyer input or the entire compliance report in a webhook URL or unrestricted message body.

#### MCP or direct library

These paths are intentionally off-platform and cannot claim platform billing or a successful Apify result. Use them for schema discovery or fail-closed integration tests. For a commercial report with platform settlement, start an Actor run.

### Operating guide

#### Before each run

- verify that the reporting year is supported;
- verify authorization to process every submitted fact;
- remove unnecessary identifiers and narrative;
- classify quantities honestly as measured-as-submitted, buyer-reported, or estimated;
- confirm port order and leg intervals;
- remove unsupported fuels and legal branches rather than guessing them;
- inspect the live pricing panel and set a bounded cap;
- record your planned reviewer and retention period.

#### After each run

- wait for terminal platform status;
- read `OUTPUT` and confirm it belongs to the current run storage;
- inspect `status`, `errors`, `deliveryReceipt`, and counter parity;
- compare Dataset `preparedReportDigest` with the terminal evidence projection;
- review truth classes, confidence reasons, assumptions, sources, and limitations;
- preserve ambiguous evidence without retry;
- record human approval or rejection outside the Actor;
- delete input and output according to policy when no longer needed.

#### Monitoring

Alert on source fixture holds, source freshness holds, pricing mismatches, budget stops, delivery unknown, OUTPUT write failures, counter mismatches, and rising schema rejection. Do not alert on the synthetic demo as if it were a compliance report.

#### Release management

Treat fixture, rule, contract, pricing, schema, and README changes as reviewed release inputs. Run the full test suite, official Apify schema validator, exact upload projection, one immutable build, one bounded no-retry canary, Dataset/KVS/PPE reconciliation, and only then promote that exact build.

### FAQ

#### Is this an EU or regulator product?

No. It is an independent derived-calculation tool. It is not endorsed by the European Union, European Commission, EUR-Lex, Publications Office, a verifier, carrier, exchange, or authority.

#### Does it determine legal liability?

No. It calculates a bounded exposure estimate under the pinned model and rejects unsupported branches.

#### Does it perform MRV verification?

No. `verifiedInputs` is always empty and the output says `mrv_verification_not_performed`.

#### Does it fetch AIS or carrier data?

No. Runtime makes zero provider and network requests.

#### Are official sources live?

No. Exact reviewed fixture bytes are bundled and hash checked. Their accepted-through-year boundary is explicit.

#### Is a measured quantity verified?

No. It means measured as submitted by the buyer.

#### Does a high confidence level mean legal certainty?

No. It is a deterministic assessment of the submitted truth mix and contract coverage.

#### Can I submit LNG?

Not in V1. LNG and bio-LNG are rejected because methane-slip and related formula branches are not released here.

#### Can I submit a special-port treatment?

No. The source is pinned as an exclusion boundary, but the Actor does not classify such calls.

#### Is the allowance price live?

No. It is optional buyer-supplied planning data and is not a quote.

#### Does the Dataset row prove payment?

No. It is deliberately receipt free. Read current-run KVS `OUTPUT`.

#### Can I retry `delivery_unknown`?

Not blindly. Reconcile the first run's Dataset, KVS, platform counters, and logs first.

#### Does the Actor deduplicate across runs?

No. Every run is independent and a new run can incur a new start and result charge.

#### Does it store a named baseline?

No. `stateReceipt` truthfully records a stateless single-voyage product.

#### Are hashes anonymous?

No. They are deterministic linkage values and must be handled under the same confidentiality policy as the underlying workflow.

#### Can the report trigger allowance purchase automatically?

No. It is evidence for human review, not an execution authorization.

#### What should I retain?

Retain the build/run/storage IDs, accepted input under access control, OUTPUT, Dataset row, digests, source revision, and reviewer decision for the period your policy and law require.

### Sources and rights

The bundled source registry identifies the exact legal-primary, amendment, calculation-method, GWP, special-port, official-guidance, and rights-policy materials used by V1. Each row records owner, authoritative URL, legal version, fixture capture, SHA-256, evidence-manifest digest, attribution, terms URL, licence status, accepted use, and prohibited raw redistribution.

The product emits derived numeric and decision-support output plus short attribution. It does not redistribute source HTML or legal prose, use official logos or emblems, or imply endorsement. Third-party works and separately protected material are excluded from the released output. A source row marked approved means the release evidence gate passed under the recorded policy; it is not a legal opinion for a buyer's independent use.

Buyer-provided vessel, route, quantity, and price facts remain the buyer's responsibility. You must have a lawful basis and sufficient contractual rights to submit, process, retain, and share them. Keep credentials and source documents out of input. If your workflow requires a carrier feed, verifier evidence, registry record, invoice, bunker delivery note, or market quote, obtain it through an authorized channel and review its terms separately; this Actor does not fetch or license it for you.

Official-source coverage and rights are bounded by the pinned registry and accepted-through-year value. Before extending the rule year, changing a formula, adding a fuel, handling special-port treatment, or relying on a new legal amendment, recapture authoritative sources, evaluate reuse conditions and third-party exceptions, update attribution, conduct independent review, regenerate schemas and examples, and release a new immutable build.

# Actor input Schema

## `schemaVersion` (type: `string`):

Contract version for validation and output interpretation. Enter `1.1.0`; other values are rejected before any Dataset delivery.

## `mode` (type: `string`):

Choose `decision` for buyer-supplied voyage facts or `synthetic_demo` for the fixed no-charge example. Runtime enforces the fields allowed in each mode.

## `requestId` (type: `string`):

Buyer correlation ID copied into the evidence receipt. Decision mode requires `rq_` plus 32 lowercase hexadecimal characters; demo mode uses `public-demo-v1`.

## `reportingYear` (type: `integer`):

EU ETS reporting year for the voyage decision, from 2024 onward. Runtime pins the surrender rule and phase-in percentage for the year and rejects any mismatch against `ruleVersion`.

## `ruleVersion` (type: `string`):

Pinned rule-set label for `reportingYear`, for example `eu-ets-maritime-2026-v2`. Runtime rejects the request unless this matches the year's pinned rule exactly.

## `vessel` (type: `object`):

Buyer-declared vessel facts used for in-scope screening. Required and validated only in `decision` mode; `synthetic_demo` mode uses `null`.

## `portCalls` (type: `array`):

Ordered array of 2–9 buyer-supplied port-call facts. IDs must be unique and are referenced by voyage legs and optional berth-consumption records.

## `legs` (type: `array`):

Ordered array of 1–8 voyage legs linking the supplied port calls. Each leg includes its UTC interval and bounded fuel-consumption evidence.

## `berthRecords` (type: `array`):

Optional array of up to 18 berth fuel-consumption records linked to port calls. Supply an empty array when no berth consumption evidence is available.

## `priceInput` (type: `object`):

Optional buyer-supplied EU allowance price used for cost estimation. Set null when no price evidence is supplied for this decision.

## `scenario` (type: `object`):

Optional what-if fuel-mass multiplier scenario. Set null to use the buyer-supplied facts exactly as reported, with no multiplier applied.

## Actor input object example

```json
{
  "schemaVersion": "1.1.0",
  "mode": "decision",
  "requestId": "rq_0123456789abcdef0123456789abcdef",
  "reportingYear": 2026,
  "ruleVersion": "eu-ets-maritime-2026-v2",
  "vessel": {
    "imoNumber": "1234567",
    "grossTonnage": 5000,
    "shipCategory": "cargo",
    "excludedClass": "none",
    "article12Treatment": "none_declared"
  },
  "portCalls": [
    {
      "portCallId": "p1",
      "unlocode": "NLRTM",
      "memberStatePort": true,
      "isPortOfCall": true,
      "specialTreatment": "ordinary",
      "startedAt": "2026-03-01T00:00:00Z",
      "endedAt": "2026-03-01T12:00:00Z"
    },
    {
      "portCallId": "p2",
      "unlocode": "USLAX",
      "memberStatePort": false,
      "isPortOfCall": true,
      "specialTreatment": "ordinary",
      "startedAt": "2026-03-05T00:00:00Z",
      "endedAt": "2026-03-05T12:00:00Z"
    }
  ],
  "legs": [
    {
      "legId": "l1",
      "departurePortCallId": "p1",
      "arrivalPortCallId": "p2",
      "departureAt": "2026-03-01T12:00:00Z",
      "arrivalAt": "2026-03-05T00:00:00Z",
      "fuelRecords": [
        {
          "recordId": "f1",
          "consumptionStartAt": "2026-03-01T12:00:00Z",
          "consumptionEndAt": "2026-03-04T12:00:00Z",
          "fuelType": "HFO",
          "massTonnes": "100",
          "dataTruth": "measured"
        }
      ]
    }
  ],
  "berthRecords": [
    {
      "recordId": "b1",
      "portCallId": "p1",
      "consumptionStartAt": "2026-03-01T01:00:00Z",
      "consumptionEndAt": "2026-03-01T03:00:00Z",
      "fuelType": "MDO_MGO",
      "massTonnes": "10",
      "dataTruth": "measured"
    }
  ],
  "priceInput": {
    "currency": "USD",
    "usdPerAllowance": "100",
    "priceDate": "2026-03-05",
    "priceObservedAt": "2026-03-05T12:00:00Z",
    "dataTruth": "buyer_reported"
  },
  "scenario": {
    "alternativeFuelMassMultiplier": "1"
  }
}
```

# Actor output Schema

## `OUTPUT` (type: `string`):

KVS record with evidence, billing, delivery, and state receipt.

## `REPORTS` (type: `string`):

Default Dataset items are receipt-free prepared evidence written before the pinned SDK charge; terminal OUTPUT is authoritative for confirmed delivery and billing.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "schemaVersion": "1.1.0",
    "mode": "decision",
    "requestId": "rq_0123456789abcdef0123456789abcdef",
    "reportingYear": 2026,
    "ruleVersion": "eu-ets-maritime-2026-v2",
    "vessel": {
        "imoNumber": "1234567",
        "grossTonnage": 5000,
        "shipCategory": "cargo",
        "excludedClass": "none",
        "article12Treatment": "none_declared"
    },
    "portCalls": [
        {
            "portCallId": "p1",
            "unlocode": "NLRTM",
            "memberStatePort": true,
            "isPortOfCall": true,
            "specialTreatment": "ordinary",
            "startedAt": "2026-03-01T00:00:00Z",
            "endedAt": "2026-03-01T12:00:00Z"
        },
        {
            "portCallId": "p2",
            "unlocode": "USLAX",
            "memberStatePort": false,
            "isPortOfCall": true,
            "specialTreatment": "ordinary",
            "startedAt": "2026-03-05T00:00:00Z",
            "endedAt": "2026-03-05T12:00:00Z"
        }
    ],
    "legs": [
        {
            "legId": "l1",
            "departurePortCallId": "p1",
            "arrivalPortCallId": "p2",
            "departureAt": "2026-03-01T12:00:00Z",
            "arrivalAt": "2026-03-05T00:00:00Z",
            "fuelRecords": [
                {
                    "recordId": "f1",
                    "consumptionStartAt": "2026-03-01T12:00:00Z",
                    "consumptionEndAt": "2026-03-04T12:00:00Z",
                    "fuelType": "HFO",
                    "massTonnes": "100",
                    "dataTruth": "measured"
                }
            ]
        }
    ],
    "berthRecords": [
        {
            "recordId": "b1",
            "portCallId": "p1",
            "consumptionStartAt": "2026-03-01T01:00:00Z",
            "consumptionEndAt": "2026-03-01T03:00:00Z",
            "fuelType": "MDO_MGO",
            "massTonnes": "10",
            "dataTruth": "measured"
        }
    ],
    "priceInput": {
        "currency": "USD",
        "usdPerAllowance": "100",
        "priceDate": "2026-03-05",
        "priceObservedAt": "2026-03-05T12:00:00Z",
        "dataTruth": "buyer_reported"
    },
    "scenario": {
        "alternativeFuelMassMultiplier": "1"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/voyage-carbon-compliance-evidence").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "schemaVersion": "1.1.0",
    "mode": "decision",
    "requestId": "rq_0123456789abcdef0123456789abcdef",
    "reportingYear": 2026,
    "ruleVersion": "eu-ets-maritime-2026-v2",
    "vessel": {
        "imoNumber": "1234567",
        "grossTonnage": 5000,
        "shipCategory": "cargo",
        "excludedClass": "none",
        "article12Treatment": "none_declared",
    },
    "portCalls": [
        {
            "portCallId": "p1",
            "unlocode": "NLRTM",
            "memberStatePort": True,
            "isPortOfCall": True,
            "specialTreatment": "ordinary",
            "startedAt": "2026-03-01T00:00:00Z",
            "endedAt": "2026-03-01T12:00:00Z",
        },
        {
            "portCallId": "p2",
            "unlocode": "USLAX",
            "memberStatePort": False,
            "isPortOfCall": True,
            "specialTreatment": "ordinary",
            "startedAt": "2026-03-05T00:00:00Z",
            "endedAt": "2026-03-05T12:00:00Z",
        },
    ],
    "legs": [{
            "legId": "l1",
            "departurePortCallId": "p1",
            "arrivalPortCallId": "p2",
            "departureAt": "2026-03-01T12:00:00Z",
            "arrivalAt": "2026-03-05T00:00:00Z",
            "fuelRecords": [{
                    "recordId": "f1",
                    "consumptionStartAt": "2026-03-01T12:00:00Z",
                    "consumptionEndAt": "2026-03-04T12:00:00Z",
                    "fuelType": "HFO",
                    "massTonnes": "100",
                    "dataTruth": "measured",
                }],
        }],
    "berthRecords": [{
            "recordId": "b1",
            "portCallId": "p1",
            "consumptionStartAt": "2026-03-01T01:00:00Z",
            "consumptionEndAt": "2026-03-01T03:00:00Z",
            "fuelType": "MDO_MGO",
            "massTonnes": "10",
            "dataTruth": "measured",
        }],
    "priceInput": {
        "currency": "USD",
        "usdPerAllowance": "100",
        "priceDate": "2026-03-05",
        "priceObservedAt": "2026-03-05T12:00:00Z",
        "dataTruth": "buyer_reported",
    },
    "scenario": { "alternativeFuelMassMultiplier": "1" },
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/voyage-carbon-compliance-evidence").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "schemaVersion": "1.1.0",
  "mode": "decision",
  "requestId": "rq_0123456789abcdef0123456789abcdef",
  "reportingYear": 2026,
  "ruleVersion": "eu-ets-maritime-2026-v2",
  "vessel": {
    "imoNumber": "1234567",
    "grossTonnage": 5000,
    "shipCategory": "cargo",
    "excludedClass": "none",
    "article12Treatment": "none_declared"
  },
  "portCalls": [
    {
      "portCallId": "p1",
      "unlocode": "NLRTM",
      "memberStatePort": true,
      "isPortOfCall": true,
      "specialTreatment": "ordinary",
      "startedAt": "2026-03-01T00:00:00Z",
      "endedAt": "2026-03-01T12:00:00Z"
    },
    {
      "portCallId": "p2",
      "unlocode": "USLAX",
      "memberStatePort": false,
      "isPortOfCall": true,
      "specialTreatment": "ordinary",
      "startedAt": "2026-03-05T00:00:00Z",
      "endedAt": "2026-03-05T12:00:00Z"
    }
  ],
  "legs": [
    {
      "legId": "l1",
      "departurePortCallId": "p1",
      "arrivalPortCallId": "p2",
      "departureAt": "2026-03-01T12:00:00Z",
      "arrivalAt": "2026-03-05T00:00:00Z",
      "fuelRecords": [
        {
          "recordId": "f1",
          "consumptionStartAt": "2026-03-01T12:00:00Z",
          "consumptionEndAt": "2026-03-04T12:00:00Z",
          "fuelType": "HFO",
          "massTonnes": "100",
          "dataTruth": "measured"
        }
      ]
    }
  ],
  "berthRecords": [
    {
      "recordId": "b1",
      "portCallId": "p1",
      "consumptionStartAt": "2026-03-01T01:00:00Z",
      "consumptionEndAt": "2026-03-01T03:00:00Z",
      "fuelType": "MDO_MGO",
      "massTonnes": "10",
      "dataTruth": "measured"
    }
  ],
  "priceInput": {
    "currency": "USD",
    "usdPerAllowance": "100",
    "priceDate": "2026-03-05",
    "priceObservedAt": "2026-03-05T12:00:00Z",
    "dataTruth": "buyer_reported"
  },
  "scenario": {
    "alternativeFuelMassMultiplier": "1"
  }
}' |
apify call zinin/voyage-carbon-compliance-evidence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/voyage-carbon-compliance-evidence"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7cHAxre2btyAkxbrs/builds/iigAfJhg79gbmqhys/openapi.json
