S2S Quickstart: call the API in 5 minutes

This guide helps system‑to‑system integrators authenticate, validate a payload against EMSA MIG, and submit their first formality to Finland’s Maritime Single Window (MNSW/NEMO).

You’ll use OAuth2 client credentials, the National Declarant API, and the MIG 2.0.0.0 schemas.

Prerequisites

Environments

External users: STG and PRD only. (DEV/TST are internal to the MNSW team.)
Environment Base URL Auth Notes
STG https://mnsw-stg.mnsw.fi/ OAuth2 (client credentials) Active
PRD https://mnsw.mnsw.fi/ OAuth2 (client credentials) Onboarding required
Auth

Token endpoint

Exact issuer and token URL are shared during onboarding. Replace <TOKEN_URL> in examples accordingly.

Architecture at a glance

sequenceDiagram
  autonumber
  participant SYS as Your System
  participant IMS as Keycloak (Auth)
  participant NEMO as National declarant API
  participant MIG as MIG Schema

  SYS->>IMS: Client Credentials (client_id, client_secret)
  IMS-->>SYS: access_token (JWT)
  SYS->>NEMO: GET /portcalls (Authorization: Bearer)
  NEMO-->>SYS: 200 OK (portcalls)
  SYS->>SYS: Build Form.
  SYS->>MIG: Validate JSON against MIG 2.0.0.0 schema
  SYS->>NEMO: POST/PUT formality with MAI header + payload
  NEMO-->>SYS: 201/200 (+ validation errors if any)
      

API cards: what they are

A quick map for developers
NEMO

National API — Formalities

Create and update EMSWe formalities (e.g., NOA, NOD, CGA, CGD, ATA, ATD, CRT). Payloads are validated against EMSA MIG plus national rules. Supports presigned file uploads for attachments. Auth: OAuth2 client credentials + organizationBusinessId header.

NSD

Vessel Registry (read‑only)

Look up vessel identifiers and metadata (IMO/MMSI/call sign) to enrich your formalities and perform validation. Read-only GET endpoints. Auth: OAuth2 client credentials.

If you’re building an S2S integration, start with NEMO (National API) for formalities, use NSD for lookups, and consider AS4 only when required by your architecture.

Step‑by‑step

1) Get an access token

We use Keycloak (realm: nemo) with the OAuth2 Client Credentials grant. Use the client issued for your organization (declarant or port). The flow is identical for both.

Token endpoints
  • STG: https://mnsw-stg.mnsw.fi/keycloak/realms/nemo/protocol/openid-connect/token
  • PRD: https://mnsw.mnsw.fi/keycloak/realms/nemo/protocol/openid-connect/token

Note

Only STG and PRD are supported for external integrators. DEV/TST are for the MNSW team only.
curl
ENV=stg   # or prd
case "$ENV" in
  stg) IDP_HOST=mnsw-stg.mnsw.fi/keycloak ;;
  prd) IDP_HOST=mnsw.mnsw.fi/keycloak ;;
  *) echo "Use stg or prd (DEV/TST are internal)"; exit 1 ;;
esac

TOKEN_URL="https://$IDP_HOST/realms/nemo/protocol/openid-connect/token"
CLIENT_ID=<client_id>
CLIENT_SECRET=<secret>

ACCESS_TOKEN=$(curl -sS -X POST "$TOKEN_URL" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET" | jq -r .access_token)

echo "Token prefix:" $(echo "$ACCESS_TOKEN" | cut -c1-20)"…"

When calling NEMO APIs, include the header organizationBusinessId: <your_org_id> along with Authorization: Bearer <token>.

2) Smoke test your token

Call a simple authorized endpoint like codelist names to verify headers and scopes:

export BASE=https://mnsw-stg.mnsw.fi
export ACCESS_TOKEN=<paste_token_here>
export ORG_BID=<your_organizationBusinessId>

curl -i "$BASE/nemo-ui/api/v1/codelists/names" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "organizationBusinessId: $ORG_BID"

3) Build a minimal formality payload

Start from EMSA MIG JSON Schemas (e.g., NOA, NOD, CGD) and populate required fields. Follow national rules where applicable.

Example: skeleton for NOA (arrival notification)
{
  "MAI": {
    "msgFunction": "9",  // original
    "msgId": "<uuid>",
    "msgSender": "<EORI/BusinessId>",
    "msgRecipient": "MNSW-FI",
    "msgType": "NOA",
    "msgDateTime": "<ISO 8601>"
  },
  "NOA": {
    "voyage": {
      "imoNumber": "<string>",
      "vesselName": "<string>",
      "callSign": "<string>"
    },
    "port": {
      "unLoCode": "FIHEL",
      "eta": "<ISO 8601>"
    }
  }
}

Rule MIG‑P33 — Unknown data: if a value for an optional element is unknown, omit the element. Do not send empty strings, 0 or null.

4) Submit the formality

Submit using the National API endpoint for a port call. Use a stable formalityId (UUID) to enable updates (idempotency):

export VISIT_ID=<portcall-visitId>
export FORM_TYPE=NOA  # e.g., NOA, NOD, CGA, CGD, ATA, ATD…
export FORM_ID=$(uuidgen)  # or your own stable UUID

curl -i -X POST "$BASE/declarant-api/portcalls/$VISIT_ID/formalities/$FORM_TYPE" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "organizationBusinessId: $ORG_BID" \
  --data-binary @payload.json

# Update the same formality later (PUT):
curl -i -X PUT "$BASE/declarant-api/portcalls/$VISIT_ID/formalities/$FORM_TYPE/$FORM_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "organizationBusinessId: $ORG_BID" \
  --data-binary @payload.json

5) Validate and troubleshoot

Alternative channel: AS4 (RIM/Domibus)

If you integrate via AS4, package the same MIG payload as the business document and send via Domibus/RIM. Error semantics mirror REST responses.

flowchart LR
  A[Your Backoffice] -- AS4 Msg --> B(Domibus/RIM)
  B -- RIM Msg --> C[MNSW Core]
  C -- Ack / Business Response --> B
  B -- Status/Errors --> A
        

FAQ

What scopes/claims are required?

Scopes are provisioned with your client during onboarding. Always include organizationBusinessId header matching your organization.

How do I find the right schema version?

Use MIG 2.0.0.0 unless instructed otherwise in release notes. For breaking changes, see the Changelog.

References