EMSWe · NEMO Developer Hub

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.1.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

National API integration flow Your system requests an OAuth2 access token from Keycloak, builds and validates the formality against the MIG schema, submits it to the National Declarant API, and receives a response with any validation errors. 1. Your system: authenticate Keycloak: access token (JWT) 2. Build formality payload Validate against MIG schema 3. Submit formality National Declarant API Response: success or validation errors

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 --fail-with-body -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 -er '.access_token // empty')

When calling NEMO APIs, include the header organizationBusinessId: <your_org_id> along with an OAuth2 bearer token. Do not log tokens or client secrets.

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" \
  --oauth2-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.

Use the exact envelope, element names, and namespaces from the NOA JSON Schema. Save the complete, validated document as payload.json; a simplified illustrative object is not a valid submission.

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, with the JSON document in the formalityData multipart field. Use the existing formalityId when updating:

export VISIT_ID=<portcall-visitId>
export FORM_TYPE=NOA  # e.g., NOA, NOD, CGA, CGD, ATA, ATD…
export FORM_ID=<existing-formality-uuid>

curl -i -X POST "$BASE/declarant-api/v1/portcalls/$VISIT_ID/formalities/$FORM_TYPE" \
  --oauth2-bearer "$ACCESS_TOKEN" \
  -H "organizationBusinessId: $ORG_BID" \
  -F "formalityData=@payload.json;type=application/json"

# Update the same formality later (PUT):
curl -i -X PUT "$BASE/declarant-api/v1/portcalls/$VISIT_ID/formalities/$FORM_TYPE/$FORM_ID" \
  --oauth2-bearer "$ACCESS_TOKEN" \
  -H "organizationBusinessId: $ORG_BID" \
  -F "formalityData=@payload.json;type=application/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.

AS4 message and response flow Your back office sends an AS4 message through Domibus/RIM to MNSW Core. Acknowledgements and business responses return through Domibus/RIM as status information or errors. Your back office Domibus/RIM MNSW Core AS4 / RIM message Acknowledgements, business responses, status and errors

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.1.0 schemas published by the NEMO Developer Hub. For breaking changes, see the Changelog.

References