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
- Issued client_id and client_secret (request via onboarding and 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
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 --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
- Validate JSON against the MIG 2.0.1.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.
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.