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
- Issued client_id and client_secret (request via Get access), and your
organizationBusinessId. - Network egress to the selected environment and token endpoint (provided during onboarding).
- Tooling:
curlor Postman, and a JSON editor/validator.
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 |
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 developersNational 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.
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.
- 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.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
- Validate JSON against MIG 2.0.0.0 schema for the formality type.
- On 4xx errors, check response body for rule violations (EMSA MIG vs. national).
- Use request‑id (if returned in headers/body) when contacting support.
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.