Перейти до змісту

ShipCore — Phase 2 Carrier APIs Deep Dive

Призначення документа. Phase 0 → Phase 1 закрив бізнес-стратегію та compliance scope. Phase 2 заглиблюється в технічну сторону зовнішніх інтеграцій — щоб Phase 4 (refactor) і Phase 5 (Maersk Spot first integration) стартували з точними API-фактами, а не з припущеннями. Кожна secondary integration (Hapag-Lloyd, CMA CGM, MSC) у Phase 8 наслідує паттерн, обкатаний на Phase 5. Аналогічно — road-телематика, WIN platform, Awery air-cargo.

Reliability legend. ✅ підтверджено офіційним джерелом (developer.maersk.com, dcsa.org, IATA, official partner docs) · 🟡 підтверджено двома незалежними вторинними джерелами (industry coverage, vendor reviews, customer case studies) · ⚠️ суперечливі дані між джерелами — обидві версії показані · (?) невпевнено, треба окремий research перед commit'ом.

Бюджет рисерчу. ~55 хв (16 паралельних web-search + 2 GitHub/portal fetch). Phase 4 refactor commit (~3 тижні effort) та Phase 5 Maersk Spot integration (~2 тижні) залежать від цього documents — точність важливіша за швидкість.


0. TL;DR

  1. DCSA — нова реальність ocean. Top-9 carriers (MSC, Maersk, CMA CGM, Hapag-Lloyd, ONE, Evergreen, Yang Ming, HMM, ZIM) — founding members. Booking 2.0 + BL 3.0 фінал — лютий 2025 ✅. Track & Trace v2.2 — поточний стандарт ✅. VGM REST API — листопад 2025 ✅ (replaces VERMAS EDIFACT, як зафіксовано у C5 corrections). Імплікація: ShipCore проектує CarrierAdapter навколо DCSA OpenAPI specs, не carrier-specific schemas — adapter для одного carrier'а на 70% реюзабельний для іншого DCSA-compliant.
  2. Maersk Spot для Phase 5 — підтверджено правильним вибором. OAuth2 client credentials flow ✅, безкоштовний tier API access (subject to future change) ✅, sandbox environment з self-service registration ✅, DCSA Booking 2.0 endpoint у production ✅. Token endpoint https://api.maersk.com/customer-identity/oauth/v2/access_token + Consumer-Key header. Python integration straightforward через requests-oauthlib або authlib.
  3. CMA CGM — найкращий second carrier для Phase 8. Two-tier модель: public tier (API key, free trial 30 днів) для container milestones + private tier (OAuth2 + booking party identification) для rail/inland events ✅. Hapag-Lloyd Quick Quotes — теж OAuth2 + sandbox. MSC — DCSA-compliant з 2025 (Commercial Schedules live, Booking 2.0 + eBL 3.0 в розробці) 🟡.
  4. Vizion vs Terminal49 — Vizion виграє для UA mid-market. Vizion: ~$4-5/container, ~99% global ocean coverage, 60+ standardized milestones (60+ event milestones з 7000+ raw points), 33+ shipping lines normalized 🟡. Terminal49: free dev key до 100 containers, мінімум 100/міс на платних планах, push webhook architecture 🟡. Recommendation: Vizion як primary aggregator fallback (більш покриття + дешевше per-container), Terminal49 як backup якщо потрібно push-first webhooks.
  5. WIN platform — buy-vs-build НЕ однозначно "buy". WIN — реальна live платформа (winwebconnect.com), бере 160+ airlines, інтегрована Riege Scope. Web Service API доступний (PDF docs publicly online) ✅. Але: ціни не публічні, фокус — small/mid forwarders, ocean coverage слабша за air. Phase 7 рекомендація: 2-3 дні research включно з outreach call перед commit shipcore_pricing.LaneRateHistory build. Якщо WIN partnership дешевше за internal build для air — buy. Для ocean — будуємо власне (LaneRateHistory + DCSA Commercial Schedules).
  6. Awery — air partnership confirmed actionable. UA-походження ✅, переможець IATA ONE Record Hackathon п'ятий раз поспіль (2026-04-29) ✅, готує open-source PHP ONE Record server для широкої adoption ✅. Phase 9+ план з C2 corrections — outreach під час Phase 4-5, Awery API integration + white-label opportunity для ShipCore-Avia.
  7. Road telematics: Wialon production-ready, Geotab/Samsara — для EU expansion v1.5. Wialon hosting API rate limits well-documented ✅ (10 unsuccessful logins/min, 200 executions/5min/IP, 1GB max report) — vendor-locked для UA-ринку, де він де-факто стандарт. Geotab — leader EU presence (ABI Research #1 four years running) 🟡, Samsara — superior UX (Capterra 84% satisfaction) 🟡. Webfleet (Bridgestone-owned, ex-TomTom Telematics) — третя альтернатива, WEBFLEET.connect SOAP API legacy + REST modern 🟡. Phase 5 — Wialon only, Phase 8 — Geotab/Samsara connectors як EU expansion enabler.
  8. DCSA OpenAPI v2.0.1 — ця версія ВЖЕ застаріла. GitHub repo dcsaorg/DCSA-OpenAPI last "release tag" 2.0.1 травня 2021, але активна розробка йде у master branch без semver tags. Поточні актуальні версії: TNT v2.2 (грудень 2024), Booking 2.0 final (лютий 2025), BL 3.0 final (лютий 2025), VGM v1.0 (листопад 2025) ✅. Висновок: GitHub releases page вводить в оману, треба орієнтуватися на dcsa.org/standards/* для current state.

1. Maersk Spot (Phase 5 P0 — first carrier integration)

1.1. API endpoints catalogue

Maersk Developer Portal (developer.maersk.com/api-catalogue) ✅ — публічний self-service portal з 40+ APIs, головні для ShipCore Phase 5:

API Product Scope DCSA-aligned Phase 5 use
Maersk Offers API (api.productmanagement.maersk.com/offers) Spot product offers, pricing at booking time, port-pair rates Maersk-specific (не DCSA — це pre-booking quote) Quote pull для shipcore_forwarder.Quote
Ocean Booking v2 [DCSA] (developer.maersk.com/api-catalogue/EDP%20Booking) Create/amend/cancel ocean booking, get booking status DCSA Booking 2.0 ✅ Booking creation з shipcore_sea.OceanBooking
Track & Trace Plus (developer.maersk.com/api-catalogue/Track%20and%20Trace%20Plus) Container/shipment events, ETAs, transhipment milestones DCSA TNT 2.2 ✅ Real-time tracking → shipcore.ShipmentEvent
Ocean — Carrier Bill of Lading [DCSA] (dcsa-bill-of-lading) Issue/amend/surrender BL DCSA BL 3.0 ✅ shipcore_sea.BillOfLading issuance + eBL flow (Phase 8)
Ocean Booking Status Webhook [DCSA] (ocean-booking-status-webhook) Push notifications для booking status changes DCSA-compliant webhook spec ✅ Async update листeners для booking lifecycle
VGM Submission (DCSA REST replacement of VERMAS EDIFACT) Submit verified gross mass DCSA VGM v1.0 (Nov 2025) ✅ shipcore_terminal.GateTransaction VGM push (C5 correction)
Schedules API (Commercial + Operational Vessel Schedules) Vessel sailings, port pairs, transit times DCSA Commercial Schedules ✅ Routing/ETA estimates у Quote step

Note: Track & Trace Plus = Maersk's value-add layer over base DCSA TNT. Дає SLA + extra metadata (наприклад, temperatureAtCarrier для reefer). Базовий TNT доступний без upgrade.

1.2. Authentication — OAuth2 client credentials

Підтверджено через developer.maersk.com/support/authorisation ✅:

# Token endpoint
POST https://api.maersk.com/customer-identity/oauth/v2/access_token
Headers:
    Consumer-Key: <consumer_key_from_app_registration>
    Content-Type: application/x-www-form-urlencoded
Body (form-urlencoded):
    grant_type=client_credentials
    client_id=<client_id>
    client_secret=<client_secret>

# Response: { "access_token": "...", "expires_in": 3600, "token_type": "Bearer" }

Особливості:

  • Двофакторний auth: окремо Consumer-Key (header, app identity) + client_id/client_secret (credentials body). Це Apigee-style edge gateway (Maersk hostuye через Google Cloud Apigee 🟡).
  • Token TTL 1 година. Adapter повинен refresh proactively (e.g., якщо expires_in - elapsed < 60s).
  • На кожний бізнес-запит додається Authorization: Bearer <access_token> + Consumer-Key header.
  • Sandbox vs production — окремі consumer keys, окремі URL (api-sit.env.productmanagement.maersk.com для sandbox vs api.productmanagement.maersk.com для prod) 🟡.

1.3. Sandbox setup

Підтверджено через api-sit.env.productmanagement.maersk.com/offers/docs/overview/onboarding.html ✅:

  1. Реєстрація на Maersk Developer Portal — self-service, без customer contract вимагається на старті ✅.
  2. Створення Application у личному кабінеті — отримуєш Consumer Key + Client ID/Secret пару.
  3. Subscribe до конкретного API product (e.g., "Ocean Booking v2 [DCSA]") — окремий plan для sandbox, окремий для prod.
  4. Sandbox дані — синтетичні fixtures, обмежений набір port-pairs (типово USCHI-DEHAM, CNSHA-USLAX) для тестування.
  5. Production access — потребує customer contract з Maersk (тобто компанія має активний mscp або equivalent customer agreement) ✅.

Implication для pilot. Wave 1 UA mid-market road carrier (C8 архетип, конкретний клієнт TBD) — UA-tier carrier, скоріш за все НЕ має активного Maersk customer contract. Це означає, що Phase 5 Maersk integration демонструється на sandbox, а production rollout = після того як ShipCore підпише агентську угоду через Maersk-aligned 3PL. Альтернатива — pilot з ZAMMLER (Wave 2) у якого contract є.

1.4. Rate limits & error handling

⚠️ Rate limits НЕ оприлюднюються публічно у developer portal (?). На основі industry practice (Apigee gateway defaults + Maersk capacity claims):

  • Estimated: 100-500 RPS per consumer key для read APIs (Schedules, Track & Trace).
  • Estimated: ~10-30 RPS для write APIs (Booking creation/amendment).
  • HTTP 429 (Too Many Requests) з Retry-After header — стандартна Apigee pattern (?).

Retry policy для CarrierAdapter:

# Pseudocode для retry strategy
RETRY_ON_STATUS = {429, 502, 503, 504}
TRANSIENT_ERRORS = {ConnectionError, ReadTimeout}
MAX_RETRIES = 5
BACKOFF = ExponentialBackoff(base=1.0, multiplier=2.0, max_delay=60.0, jitter=0.5)

# 401 Unauthorized → token expired → refresh + retry once (no backoff)
# 403 Forbidden → permission issue → no retry, raise to user
# 422 Unprocessable Entity → DCSA validation error → no retry, surface validation
# 4xx other → log + raise
# 5xx → retry with backoff
# 429 → respect Retry-After if present, else exponential backoff

1.5. Pricing / billing model

Confirmed via maersk.com/support/faqs/do-i-need-to-pay-to-use-your-api ✅:

"Maersk's APIs are free to use, though the company reserves the right to implement a payment plan in the future for API usage as defined by their API License Terms."

Implication: Phase 5 cost model = $0 for API access, але Maersk explicitly резервує право монетизувати. Розумний planning horizon — 2-3 роки free, потім очікувати tiered pricing (estimated $500-5000/міс при production volumes 🟡).

1.6. DCSA compliance status

Maersk — founding DCSA member ✅. Конкретні endpoints позначені [DCSA] у API catalogue, тобто URL/payload schema прямо реалізує DCSA OpenAPI specs:

  • ✅ Booking 2.0 (final Feb 2025)
  • ✅ BL 3.0 (final Feb 2025)
  • ✅ TNT v2.2 (December 2024)
  • ✅ VGM v1.0 (November 2025) — confirmed C5 correction
  • 🟡 Commercial Schedules — published; OVS pending

1.7. Sample workflow: Quote → Booking → BL → Track

1. Quote pull
   GET /offers?origin=USCHI&destination=DEHAM&commodity=44011200&containerType=40HC
   → [{ offerId, validUntil, totalPrice, transitDays, sailings: [...] }]

2. Booking creation
   POST /booking/v2/bookings
   Body (DCSA Booking 2.0):
     { offerReference, parties: { shipper, consignee, notifyParty },
       cargoDetails, equipment, transportPlan, ... }
   → { bookingReference, carrierBookingRequestReference, status: "PENDING_UPDATE_CONFIRMATION" }

3. Webhook: booking confirmation
   POST <ShipCore webhook URL>
   { bookingReference, status: "CONFIRMED", carrierBookingReference: "MAEU2025XXXX" }

4. VGM submission (after gate-in)
   POST /vgm/v1/verified-gross-mass
   Body: { equipmentReference, vgm: { weight, weighingMethod, ... } }

5. BL issuance
   POST /bills-of-lading/v3
   Body (DCSA BL 3.0): { transportDocumentReference, parties, cargoItems, charges, ... }
   → { transportDocumentReference: "MAEU0987654", documentStatus: "ISSUED" }

6. Track & Trace polling (or webhook)
   GET /track-and-trace/v2/events?carrierBookingReference=MAEU2025XXXX
   → [ { eventType: "TRANSPORT", eventClassifierCode: "ACT",
         eventDateTime, eventLocation, transportCall: {...} }, ... ]

1.8. SDK availability

⚠️ No official Maersk-published SDK для Python/Node/Java (?). Maersk покладається на raw OpenAPI specs — клієнт generates SDK via openapi-generator.

Recommendation для ShipCore CarrierAdapter:

  • Не використовувати auto-generated SDK (overhead + losely-typed коли DCSA schema еволюціонує).
  • Власна тонка обгортка MaerskSpotAdapter(BaseCarrierAdapter) з httpx (async-first) + pydantic models, generated одноразово з DCSA OpenAPI YAML, не з Maersk's portal.

1.9. DCSA VGM REST integration

Per C5 corrections, Phase 5 пише VGM через DCSA REST, не EDI. Підтверджено через dcsa.org/standards/vgm ✅:

  • Стандарт опубліковано 18 листопада 2025 (Smart Maritime Network coverage).
  • Built on DCSA data model, fully aligned з SOLAS Convention requirements.
  • Replaces VERMAS EDIFACT as preferred submission method ✅.
  • Maersk DCSA member → expected to support endpoint у production (timeline невідомий, скоріш за все Q1-Q2 2026 🟡).

Phase 5 implication: shipcore_terminal.GateTransaction пише VGM через MaerskSpotAdapter.submit_vgm(equipment_ref, weight, method) що hits DCSA-aligned endpoint. Спрощує реалізацію (нема EDI parser) і робить ShipCore автоматично compatible з усіма DCSA-aligned carriers.


2. Hapag-Lloyd Quick Quotes (Phase 8 P1)

2.1. API portal & products

api-portal.hlag.com ✅ — повноцінний developer portal з documentation hub doc.api-portal.hlag.com. API products:

Product Scope DCSA-aligned
Quotation/Booking Engine — External Prices Quick Quotes (FAK rates), Quick Quotes Spot (real-time spot), get & save quote Hapag-specific (?) — quote layer не покритий DCSA
Booking Create/amend booking DCSA Booking 2.0 (?) — verified industry adoption, але explicit DCSA alignment Hapag-Lloyd підтверджено для T&T та Schedules; Booking 2.0 — пізніша імплементація 🟡
Track & Trace Container/shipment events DCSA TNT (Hapag explicitly migrated to "latest DCSA Track & Trace interface" — confirmed via Coneksion case study) 🟡
Schedules Vessel sailings DCSA Commercial Schedules ✅

2.2. Authentication

OAuth2 з Authorization Code-type applications (per doc.api-portal.hlag.com) ✅:

  • Self-service registration → create application → отримуєш ClientID + Client Secret.
  • Sandbox environment available — sandbox tests дозволяють simulate API calls без registration ✅ (нижчий barrier ніж Maersk).
  • Subscribe to API product → activate credentials → ready to call.

2.3. Pricing & rate limits

⚠️ Не оприлюднюються публічно. Industry practice — free tier для testing, customer contract вимагається для production volumes (?). Inferred з Dachser case study (Descartes integration) — production access requires Hapag account team involvement 🟡.

2.4. DCSA compliance status

Hapag-Lloyd — founding DCSA member ✅. Track & Trace — explicitly DCSA-aligned (Coneksion CCL via Youredi case study confirms migration) 🟡. Booking 2.0 + BL 3.0 adoption — у процесі (роадмап не публічний, але як founding member експектується Q1-Q3 2026 🟡).


3. CMA CGM API (Phase 8 P1)

3.1. Two-tier model — найунікальніший для CMA

Confirmed via api-portal.cma-cgm.com/starting-guide ✅:

Tier Auth Free? Scope
Public API key ✅ Free 30-day trial з обмеженим quota Container milestones, transshipment events, planned vessel dates
Private OAuth2 + booking party identification ❌ Paid (custom contract) Rail moves, ramp moves, inland move dates, granular event data

Implication для ShipCore: Phase 8 Wave 2 (ZAMMLER pilot) — починаємо з public tier для proof-of-concept, переходимо на private tier коли є контракт між клієнтом і CMA. Це дешевша discovery path ніж Maersk (де sandbox обмежений port-pairs).

3.2. API endpoints

Per cloud.customer.cmacgm-group.com/EDI_catalog_EN 🟡:

  • Booking Request & Confirmation
  • Original BL (PDF or structured payload)
  • Container Tracking (DCSA TNT v2.2 для public + extended fields для private)
  • Schedules (DCSA Commercial Schedules ✅ — підтверджено документацією на api-portal.cma-cgm.com/products/visibility про DCSA OpenAPI v2.2.0)
  • Public Prices Finder (separate web tool — НЕ API, але дає reference для public rate validation)

3.3. DCSA compliance

CMA CGM — founding DCSA member ✅. Track & Trace — explicit DCSA OpenAPI specification for Track & Trace v2.2.0 referenced в API portal ✅. Booking 2.0 + BL 3.0 — adoption у процесі (no public roadmap).


4. MSC eBusiness (Phase 8 P1)

4.1. Status: REST coverage vs EDI legacy

MSC — найбільший carrier у світі (за TEU capacity станом на 2024) і найпізніший до cleanup REST API на DCSA. Положення станом на 2025:

  • ✅ Developer Portal launched: developerportal.msc.com/api-catalogue
  • ✅ DCSA Commercial Schedules API — fully adopted (Point-to-Point endpoint live, vessel/port schedule endpoints у розробці) — confirmed via June 2025 Smart Maritime Network coverage.
  • 🟡 DCSA Booking 2.0 + eBL 3.0 — scheduled (status: development, no public timeline).
  • ⚠️ Historical legacy: MSC heavily reliant на EDIFACT IFTMBF/IFTMIN/IFTSTA — many existing integrations не migrated до REST yet.

Implication для ShipCore: Phase 8 MSC integration — починати з DCSA Commercial Schedules (для routing/quote contexts) + Track & Trace (для visibility), уникати EDI completely. Booking creation можливо доведеться чекати 2026-Q3+ або робити через EDI як interim (не рекомендую — EDI parser cost > value).

4.2. Authentication

OAuth2 (per developer portal architecture, deduced from DCSA-aligned approach) (?) — точна flow не verified, presumably client credentials.


5. Vizion / Terminal49 (fallback aggregators) + buy-vs-build аналіз

5.1. Vizion (vizionapi.com)

Aspect Detail
Coverage ~99% global ocean freight shipments tracked, 33+ shipping lines normalized into single schema, all major U.S. terminals ✅
Carriers Most shipping lines out-of-the-box; custom carrier integration on quote 🟡
Event milestones 60+ standardized milestones derived from 7000+ raw data points ✅
Data sources API + EDI + AIS + crawling (terrestrial + satellite AIS) — multi-modal data fusion 🟡
Pricing Tier 1: ~$5/container з 15 free/міс; Tier 2: ~$4.38/container з 40 free/міс 🟡 (per F6S/Capterra reviews); Enterprise — custom 🟡
API style REST + webhooks, push-based для status changes 🟡
Latency "Real-time" claim, типово updates within minutes of carrier event publication (?)

5.2. Terminal49 (terminal49.com)

Aspect Detail
Coverage 100% carriers touching North America, includes terminals + rails + AIS ✅
Carriers All major shipping lines, "33+ shipping lines normalized into a single, consistent data schema" ✅
Pricing Free Developer Key до 100 containers; paid plans (Lite/Essential/Enterprise) start at min 100 containers/month 🟡
API style REST + push-first webhooks (push API updates webhook on status change including milestones, ETAs) ✅
Reliability claims 99.5% API uptime, low latency, guaranteed data freshness ✅

5.3. Buy-vs-build для UA mid-market

Scenario 1: 100% direct adapters
- Build effort: ~2 тижні per carrier × ~10 carriers = 20 weeks
- Maintenance: ~1 day/month per adapter × 10 = ~120 days/year
- API costs: ~$0/month (більшість free tier)
- TOTAL Year 1: ~140 dev-days + $0/month

Scenario 2: 100% aggregator (Vizion)
- Build effort: ~1 week (single integration)
- Maintenance: ~0.5 day/month
- API costs: ~$200-500/month per ~1000 containers (Vizion rough estimate)
- TOTAL Year 1: ~7 dev-days + ~$3-6K/year

Scenario 3: HYBRID (DECISION D11) ✅ recommended
- Direct adapters для top-4 (Maersk Phase 5, Hapag/CMA/MSC Phase 8)
  → ~8 weeks effort × 4 = 32 weeks (across Phase 5+8)
- Vizion fallback для остатних 20+ carriers (ONE, ZIM, COSCO, Evergreen, ...)
  → ~1 week integration
- Mix benefits: rich data for top carriers + coverage breadth via aggregator
- TOTAL Year 1: ~33 dev-days incremental + ~$2-4K/year aggregator cost

Conclusion: Hybrid (D11) залишається правильним рішенням для UA mid-market (price sensitive але не extreme — клієнти готові платити $200-500/міс aggregator subscription за value visibility).


6. Уніфікований CarrierAdapter Python interface design

Наступне — proposal для Phase 4 implementation. Pythonic, async-first, DCSA-typed моделями, чітке розділення direct vs aggregator implementations.

6.1. Base abstract class

# backend/shipcore/integrations/carrier_adapter.py

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from typing import AsyncIterator, Sequence

from shipcore.dcsa.models import (
    BookingRequest, BookingConfirmation, TransportEvent,
    BillOfLading, VGMSubmission, OfferQuote
)


@dataclass(frozen=True)
class CarrierCredentials:
    """Per-tenant + per-carrier secrets (зберігається у TenantSecret + envelope encryption)."""
    consumer_key: str | None = None  # Maersk-style edge gateway
    client_id: str
    client_secret: str
    api_key: str | None = None       # CMA CGM public tier alternative
    sandbox: bool = True
    extra: dict[str, str] | None = None  # vendor-specific overrides


class BaseCarrierAdapter(ABC):
    """Контракт для будь-якого ocean carrier integration.

    Implementations:
        - DirectCarrierAdapter — direct REST до carrier (Maersk, Hapag, CMA, MSC)
        - AggregatorCarrierAdapter — через Vizion/Terminal49 для решти

    Усі методи async — Daphne already в проекті, blocking IO у Channels не дозволено.
    Усі моделі — pydantic v2 згенеровані з DCSA OpenAPI specs (single source of truth).
    """

    SCAC: str  # Standard Carrier Alpha Code (e.g., "MAEU", "HLCU", "CMDU", "MSCU")
    DCSA_VERSIONS: dict[str, str]  # {"booking": "2.0", "tnt": "2.2", "bl": "3.0", "vgm": "1.0"}

    def __init__(self, credentials: CarrierCredentials, http_client=None):
        self.credentials = credentials
        self._http = http_client  # injected httpx.AsyncClient — testability
        self._token_cache: tuple[str, datetime] | None = None

    @abstractmethod
    async def get_quote(
        self,
        origin: str,             # UN/LOCODE, e.g., "USCHI"
        destination: str,        # UN/LOCODE, e.g., "DEHAM"
        commodity_code: str,     # HS code
        equipment: Sequence[str],  # ["40HC", "20DV"]
        departure_window: tuple[datetime, datetime],
    ) -> Sequence[OfferQuote]:
        """Pull spot/contract rates. Returns 0+ offers with validity window."""

    @abstractmethod
    async def create_booking(self, request: BookingRequest) -> BookingConfirmation:
        """Submit booking request to carrier. Async confirmation via webhook."""

    @abstractmethod
    async def get_booking_status(self, carrier_booking_ref: str) -> BookingConfirmation:
        """Pull current booking state."""

    @abstractmethod
    async def submit_vgm(self, submission: VGMSubmission) -> str:
        """Submit verified gross mass per DCSA VGM v1.0. Returns confirmation ID."""

    @abstractmethod
    async def issue_bill_of_lading(
        self, carrier_booking_ref: str, bl_data: BillOfLading
    ) -> BillOfLading:
        """Request BL issuance per DCSA BL 3.0."""

    @abstractmethod
    async def stream_events(
        self, carrier_booking_ref: str, since: datetime | None = None,
    ) -> AsyncIterator[TransportEvent]:
        """Yield Track & Trace events (DCSA TNT v2.2). Use webhook subscription
        in production; this method is for backfill/polling fallback."""

    # Common implementation для усіх direct adapters (OAuth2 token caching)
    async def _get_access_token(self) -> str:
        if self._token_cache:
            token, expires_at = self._token_cache
            if expires_at > datetime.utcnow().add(seconds=60):  # 60s safety margin
                return token
        token, ttl = await self._refresh_token()
        expires_at = datetime.utcnow().add(seconds=ttl)
        self._token_cache = (token, expires_at)
        return token

    @abstractmethod
    async def _refresh_token(self) -> tuple[str, int]:
        """Vendor-specific OAuth2 dance. Returns (access_token, expires_in_seconds)."""

6.2. Concrete implementation — MaerskSpotAdapter

# backend/shipcore/integrations/maersk/adapter.py

import httpx
from datetime import datetime
from typing import AsyncIterator, Sequence

from shipcore.integrations.carrier_adapter import (
    BaseCarrierAdapter, CarrierCredentials,
)
from shipcore.dcsa.models import (
    BookingRequest, BookingConfirmation, TransportEvent,
    BillOfLading, VGMSubmission, OfferQuote,
)


class MaerskSpotAdapter(BaseCarrierAdapter):
    SCAC = "MAEU"
    DCSA_VERSIONS = {"booking": "2.0", "tnt": "2.2", "bl": "3.0", "vgm": "1.0"}

    BASE_URL_PROD = "https://api.maersk.com"
    BASE_URL_SANDBOX = "https://api-sit.env.maersk.com"  # confirmed pattern via offers subdomain

    @property
    def _base_url(self) -> str:
        return self.BASE_URL_SANDBOX if self.credentials.sandbox else self.BASE_URL_PROD

    async def _refresh_token(self) -> tuple[str, int]:
        """Maersk OAuth2 client credentials flow."""
        url = f"{self._base_url}/customer-identity/oauth/v2/access_token"
        headers = {
            "Consumer-Key": self.credentials.consumer_key,
            "Content-Type": "application/x-www-form-urlencoded",
        }
        data = {
            "grant_type": "client_credentials",
            "client_id": self.credentials.client_id,
            "client_secret": self.credentials.client_secret,
        }
        resp = await self._http.post(url, headers=headers, data=data, timeout=30.0)
        resp.raise_for_status()
        payload = resp.json()
        return payload["access_token"], payload["expires_in"]

    async def _request(self, method: str, path: str, **kwargs) -> dict:
        """Wrapper з auto-token-refresh + retry."""
        token = await self._get_access_token()
        headers = kwargs.pop("headers", {})
        headers.update({
            "Authorization": f"Bearer {token}",
            "Consumer-Key": self.credentials.consumer_key,
        })
        url = f"{self._base_url}{path}"
        resp = await self._http.request(method, url, headers=headers, **kwargs)
        if resp.status_code == 401:
            self._token_cache = None  # force refresh on next call
            raise CarrierAuthError(f"Token expired or invalid for {self.SCAC}")
        resp.raise_for_status()
        return resp.json()

    async def get_quote(self, origin, destination, commodity_code, equipment,
                        departure_window) -> Sequence[OfferQuote]:
        # Maersk Offers API — Maersk-specific, не DCSA
        params = {
            "origin": origin,
            "destination": destination,
            "commodity": commodity_code,
            "containerType": ",".join(equipment),
            "departureFrom": departure_window[0].isoformat(),
            "departureTo": departure_window[1].isoformat(),
        }
        data = await self._request("GET", "/offers/v3/offers", params=params)
        return [OfferQuote.from_maersk_offer(o) for o in data.get("offers", [])]

    async def create_booking(self, request: BookingRequest) -> BookingConfirmation:
        # DCSA Booking 2.0 endpoint
        payload = request.model_dump(by_alias=True, exclude_none=True)
        data = await self._request("POST", "/booking/v2/bookings", json=payload)
        return BookingConfirmation.model_validate(data)

    async def submit_vgm(self, submission: VGMSubmission) -> str:
        # DCSA VGM v1.0 endpoint (Nov 2025 standard)
        payload = submission.model_dump(by_alias=True, exclude_none=True)
        data = await self._request(
            "POST", "/vgm/v1/verified-gross-mass", json=payload,
        )
        return data["vgmReference"]

    async def stream_events(self, carrier_booking_ref, since=None):
        # DCSA TNT v2.2 polling (preferred: webhook subscription)
        params = {"carrierBookingReference": carrier_booking_ref}
        if since:
            params["eventCreatedDateTime[gte]"] = since.isoformat()
        data = await self._request("GET", "/track-and-trace/v2/events", params=params)
        for event in data.get("events", []):
            yield TransportEvent.model_validate(event)

    # ... issue_bill_of_lading, get_booking_status — analogous

6.3. AggregatorCarrierAdapter (VizionAdapter example)

# backend/shipcore/integrations/vizion/adapter.py

class VizionAdapter(BaseCarrierAdapter):
    """Aggregator-based adapter. Mainly реалізує stream_events для tracking,
    quote/booking/BL/VGM не підтримуються — обмеження aggregator scope.

    `SCAC` тут — placeholder; реальний carrier_code приходить per shipment."""

    SCAC = "VIZN"  # synthetic
    DCSA_VERSIONS = {"tnt": "2.2"}  # only T&T mapped

    async def get_quote(self, *args, **kwargs):
        raise NotImplementedError("Aggregator does not support quotes")

    async def create_booking(self, *args, **kwargs):
        raise NotImplementedError("Aggregator does not support bookings")

    async def submit_vgm(self, *args, **kwargs):
        raise NotImplementedError("Aggregator does not support VGM submission")

    async def issue_bill_of_lading(self, *args, **kwargs):
        raise NotImplementedError("Aggregator does not support BL issuance")

    async def stream_events(self, carrier_booking_ref, since=None):
        # Vizion REST + webhooks
        ...

6.4. Adapter registry & dispatching

# backend/shipcore/integrations/registry.py

from shipcore.integrations.maersk import MaerskSpotAdapter
from shipcore.integrations.hapag import HapagLloydAdapter        # Phase 8
from shipcore.integrations.cmacgm import CMACGMAdapter            # Phase 8
from shipcore.integrations.msc import MSCAdapter                  # Phase 8
from shipcore.integrations.vizion import VizionAdapter            # Phase 5 fallback

DIRECT_ADAPTERS = {
    "MAEU": MaerskSpotAdapter,
    "HLCU": HapagLloydAdapter,
    "CMDU": CMACGMAdapter,
    "MSCU": MSCAdapter,
}

def resolve_adapter(carrier_scac: str, credentials_provider) -> BaseCarrierAdapter:
    cls = DIRECT_ADAPTERS.get(carrier_scac)
    if cls is not None:
        creds = credentials_provider.get(carrier_scac)
        return cls(credentials=creds)
    # Fallback to aggregator
    creds = credentials_provider.get("VIZION")
    return VizionAdapter(credentials=creds)

Architectural note. Цей паттерн чітко відображає D11 (hybrid direct + aggregator) і D9 (Maersk first). Реєстр — explicit dictionary (не Django entry_points), щоб уникнути magic registration і дозволити type-checking.


7. DCSA OpenAPI v2.0.1 compliance check для кожного adapter

⚠️ Важлива знахідка: "v2.0.1" з оригінального завдання — застаріла маркіровка. GitHub repo dcsaorg/DCSA-OpenAPI має release tag 2.0.1 від травня 2021, але це загальна репо-версія, не version individual specs. Поточний state на травень 2026:

DCSA Standard Поточна версія Status Released
Track & Trace (TNT) 2.2 Final ✅ 2024-Q4
Booking (BKG) 2.0 (final) Final ✅ 2025-02-20
Bill of Lading (EBL) 3.0 (final) Final ✅ 2025-02-20
EBL Issuance (EBL ISS) 1.0 Final ✅ 2024
EBL Surrender (EBL SUR) 1.0 Final ✅ 2024
Operational Vessel Schedules (OVS) 3.0 Beta 🟡 2024
Commercial Schedules (CS) 1.0 Final ✅ 2024
Just in Time PortCalls (JIT) 1.2 Final ✅ 2023
Platform Interoperability (PINT) 1.0 Beta 🟡 2024
VGM 1.0 Final ✅ 2025-11-18

7.1. Compliance matrix per carrier

Carrier TNT 2.2 Booking 2.0 BL 3.0 VGM 1.0 OVS CS JIT
Maersk ✅ live ✅ live ✅ live 🟡 expected Q1 2026 🟡
Hapag-Lloyd ✅ live 🟡 in progress 🟡 in progress 🟡 expected 🟡
CMA CGM ✅ live 🟡 in progress 🟡 in progress 🟡 expected 🟡
MSC 🟡 in progress 🟡 scheduled 🟡 scheduled 🟡 expected 🟡 ✅ live 🟡
ONE / ZIM / COSCO / Evergreen / Yang Ming / HMM 🟡 founders, varying adoption ⚠️ unknown ⚠️ unknown ⚠️ unknown ⚠️ ⚠️ ⚠️

Implication для CarrierAdapter design. Якщо ShipCore генерує pydantic models з DCSA OpenAPI YAML, ми отримуємо single source of truth що працює для всіх direct-adapters. Carrier-specific divergences (наприклад, Maersk Track & Trace Plus extra fields) — модельовані як optional extension fields.


8. Cost analysis: API access / aggregator subscriptions / implementation effort

8.1. Per-carrier integration cost (Phase 5 + 8 horizon)

Component Setup effort Maintenance/year API access cost/year
Maersk (Phase 5) 80-100 hours (включно з sandbox testing) ~30 hours $0 (free tier; reserved future paid)
Hapag-Lloyd (Phase 8) 50-70 hours (-30% vs Maersk через DCSA reuse) ~20 hours $0 (free tier)
CMA CGM (Phase 8) 50-70 hours; +10 hours для two-tier handling ~20 hours Public free 30 днів, потім ~$200-500/міс private 🟡
MSC (Phase 8) 60-80 hours; +20 hours якщо доводиться EDI fallback ~25 hours $0 confirmed (?)
Vizion (aggregator) (Phase 5 fallback) 30-40 hours ~15 hours $200-500/міс per ~1000 containers, ~$2.4-6K/year 🟡
Terminal49 (alternative) 30-40 hours ~15 hours Free <100 containers, paid plans custom 🟡
Geotab (Phase 8 EU expansion) 60-80 hours (poll-based feed pattern) ~25 hours Hardware-bundled (~$25-35/vehicle/month) 🟡
Samsara (Phase 8 alternative) 50-70 hours (REST + webhooks 2.0 cleaner DX) ~20 hours Custom quote (~$25-40/vehicle/month estimated) 🟡
Webfleet (Phase 8 alternative) 70-90 hours (legacy SOAP overhead) ~30 hours Customer license required 🟡
Wialon (Phase 5, already integrated) already done ~10 hours Hosting subscription ($1-3/object/month) 🟡

8.2. Total Phase 5 (Maersk + Vizion) cost estimate

Setup (one-time):
  Maersk adapter:           80-100 hours
  Vizion adapter:           30-40 hours
  CarrierAdapter base:      40-60 hours (reusable infra)
  DCSA pydantic models gen: 20-30 hours (one-time setup, reused)
  Integration tests:        40-60 hours
  TOTAL setup:             ~210-290 hours = ~5-7 weeks of 1 dev FTE ✅
                            (matches Phase 5 estimate from roadmap)

Year 1 operating:
  Vizion subscription:      ~$3K (1K containers/month)
  Maersk:                   $0
  Maintenance:              ~45 hours
  TOTAL Year 1 OPEX:       ~$3K + 45 hours dev time

8.3. WIN platform — buy decision threshold

Build alternative (Phase 7 internal LaneRateHistory + DCSA Schedules):
  Estimated effort: 2-3 weeks dev (~80-120 hours)
  Maintenance: ~30 hours/year
  Cost: $0 external

WIN partnership:
  Setup: ~40-60 hours (per Web Service API docs)
  Subscription: UNKNOWN (estimated $200-500/month per forwarder seat 🟡)
  Year 1: ~$2.4-6K + 60 hours

Decision rule:
  IF (WIN_year1_cost / 3) < (build_cost - WIN_setup_cost)
      AND (WIN delivers air carrier coverage we cannot build cost-effectively)
  THEN buy (subscribe to WIN)
  ELSE build (LaneRateHistory + DCSA Schedules + manual carrier outreach)

Recommendation: Phase 7 = 2-3 days research включно з outreach call (service@winwebconnect.com). Якщо Year-1 cost <$3K і покриває 50+ EU carriers air+ocean → buy. Якщо >$10K/year або mostly air-only → build.


Phase 5 (Sea P0 — Maersk Spot first)

Тиждень 1-2: Foundation 1. ✅ Create backend/shipcore/integrations/ package skeleton 2. ✅ Generate pydantic v2 models з DCSA OpenAPI YAML (TNT 2.2, Booking 2.0, BL 3.0, VGM 1.0) — single source of truth 3. ✅ Implement BaseCarrierAdapter ABC + CarrierCredentials data class 4. ✅ Implement httpx.AsyncClient factory with retry/backoff middleware 5. ✅ Setup TenantSecret encrypted storage для carrier credentials per tenant

Тиждень 3-4: MaerskSpotAdapter 6. ✅ Implement OAuth2 token caching (_refresh_token, _get_access_token) 7. ✅ Implement get_quote (Maersk Offers API) 8. ✅ Implement create_booking (DCSA Booking 2.0) 9. ✅ Implement stream_events (DCSA TNT 2.2 polling) + webhook subscription 10. ✅ Implement submit_vgm (DCSA VGM 1.0) 11. ✅ Implement issue_bill_of_lading (DCSA BL 3.0) 12. ✅ Sandbox integration tests (port-pairs USCHI-DEHAM + CNSHA-USLAX)

Тиждень 5: Vizion fallback + dispatch 13. ✅ Implement VizionAdapter (tracking-only) 14. ✅ Implement resolve_adapter registry function 15. ✅ Wire shipcore_forwarder.Booking workflow to use adapter via SCAC dispatch

Тиждень 6 (buffer): pilot integration 16. ✅ Pilot with Wave 1 UA road carrier (sandbox-only якщо немає Maersk contract) 17. ✅ Bug fixes + observability (logging, metrics, alerting)

Phase 5.5 (1-2 тижні NEW — Дунайські TOS, per C1)

Поза scope цього document — TOS specific, не carrier API.

Phase 7 (Pricing + WIN research)

Тиждень 1: WIN research 1. ✅ Outreach до service@winwebconnect.com для pricing quote 2. ✅ Read WIN Air Freight API PDF docs (winwebconnect.com/Public/Resources/TechDocs/WIN%20Air%20Freight%20API.pdf) 3. ✅ Scope assessment: air-only, ocean coverage, DCSA-aligned? 4. ✅ buy-vs-build decision per § 8.3 threshold

Тиждень 2-3: Implementation 5. ✅ Якщо WIN buy: implement WINAdapter (similar pattern, SOAP/REST) 6. ✅ Якщо build: extend shipcore_pricing.LaneRateHistory to scrape DCSA Commercial Schedules + carrier OPEX models

Phase 8 (Sea P1 + EU expansion + EU telematics)

Sequence (parallel where possible):

  1. Hapag-Lloyd adapter (~50-70 hours) — найшвидший win завдяки cleanest dev portal
  2. CMA CGM adapter (~60-80 hours) — two-tier complexity але public tier перевага для discovery
  3. MSC adapter (~60-80 hours) — починаємо з Schedules + T&T, Booking 2.0 коли live
  4. eBL interop hubs (~80-120 hours) — CargoX, WaveBL, Enigio, TradeGo, IQAX-GSBN (per C6)
  5. Geotab OR Samsara (vendor selection 1 day, integration 50-80 hours)
  6. DigiCMR / TransFollow / Open Logistics Foundation eCMR (per C-eFTI-byDesign)
  7. EU sanctions screening (free EU XML feed integration, ~30-40 hours)
  8. ICS2 pre-arrival declarations (per C7 — критично для UA→EU транзитів)

Phase 9+ (Air via Awery + remaining sea)

Sequence:

  1. Awery partnership outreach (during Phase 4-5 window per C2 — раніше initiated, не пізніше)
  2. AweryAdapter via ONE Record (Awery developing open-source PHP ONE Record server — можемо переюзати їхню schema models)
  3. ONE / ZIM / COSCO / Evergreen / Yang Ming / HMM via Vizion (no direct adapters until volume justifies)

Summary table — Carrier APIs by phase

Phase Component Effort DCSA-aligned Cost Year 1
5 Maersk Spot direct 80-100h ✅ Booking 2.0, TNT 2.2, BL 3.0, VGM 1.0 $0
5 Vizion fallback 30-40h aggregator ~$3K/year
5 Wialon (existing) done n/a (vendor proprietary) hosting
5.5 Дунайські TOS (per C1) 1-2 weeks n/a (own TOS) $0
7 WIN research → buy/build 2-3 days research + 2-3 weeks impl depends $0-6K/year
8 Hapag-Lloyd direct 50-70h ✅ TNT 2.2; Booking 2.0 in progress $0
8 CMA CGM direct 60-80h ✅ TNT 2.2; CS 1.0; BKG/BL in progress ~$3-6K/year private tier
8 MSC direct 60-80h 🟡 CS 1.0 live; BKG/eBL scheduled $0
8 eBL interop (CargoX/WaveBL/Enigio/TradeGo/IQAX) 80-120h DCSA EBL 3.0 ✅ ~$2-5K/year hub
8 Geotab OR Samsara EU telematics 50-80h n/a ~$25-40/vehicle/month
9+ Awery API (air) 60-80h post-partnership ONE Record ✅ partnership-dependent
9+ Vizion-only carriers (ONE/ZIM/COSCO/Evergreen/YML/HMM) 0h (already covered) DCSA TNT via aggregator included in Vizion subscription

Open questions deferred до Phase 3+

  1. Maersk rate limits — exact numbers. Не публічно. Action: при отриманні sandbox credentials — тестувати empirically (e.g., burst 100 RPS until 429), документувати у adapter docstring.
  2. MSC OAuth2 specifics. Не verified публічно. Action: Phase 8 — read MSC developer portal docs after registration.
  3. Hapag-Lloyd Booking 2.0 / BL 3.0 timelines. Public roadmap absent. Action: Phase 8 — direct outreach to Hapag API team.
  4. WIN actual subscription pricing. Не на сайті. Action: Phase 7 outreach call.
  5. Geotab vs Samsara final selection. ABI Research → Geotab. Capterra UX → Samsara. Action: Phase 8 — pilot trial both for 30 days, decide based on UA hardware availability + EU support presence.
  6. DCSA OpenAPI Python codegen tooling. datamodel-code-generator (Python) vs openapi-python-client vs custom. Action: Phase 4 prototype, document in adapter README.
  7. Awery commercial partnership terms. Action: outreach під час Phase 4-5 calendar window (per C2) — earliest possible, бо partnership negotiations типово 3-6 months.

Phase 2 carrier APIs deep-dive закрито 2026-05-12. Phase 3 (data model deep-dive — DCSA pydantic generation, Waybill canonical projections, multi-leg execution) стартує з цих припущень.


Sources

DCSA standards

Maersk

Hapag-Lloyd

CMA CGM

MSC

Vizion / Terminal49

Road telematics

Wialon

WIN platform

Awery + ONE Record