Your first call against the sandbox
From zero to reading consumption data in four shell commands. Every step is copy-pasteable — pick a shell that has curl, python3, and openssl (macOS, Linux, or WSL).
1Start the OAuth flow
The sandbox uses OAuth 2.1 Authorization Code + PKCE. First, generate a PKCE verifier + challenge pair — this proves later that the client that requested the code is the one redeeming it.
VERIFIER=$(openssl rand -base64 48 | tr -d '=+/' | head -c 64)
CHALLENGE=$(printf "%s" "$VERIFIER" | openssl dgst -binary -sha256 \
| openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n')
echo "Verifier: $VERIFIER"
echo "Challenge: $CHALLENGE"
In a real ERP flow the user's browser would redirect to /oauth/authorize, they'd pick Properties in the consent screen, and the authorization server would redirect back with a code. For the quickstart we compress those steps — we simulate "user approves the consent" with a single POST:
CODE=$(curl -s -D - -X POST \
-d "client_id=sandbox-demo-client" \
-d "redirect_uri=http://localhost:3000/callback" \
-d "scope=read:units read:devices read:readings" \
-d "state=quickstart" \
-d "code_challenge=$CHALLENGE" \
-d "code_challenge_method=S256" \
-d "property=6fb74d25-5517-4dcc-ae8b-0914579ab036" \
-d "decision=approve" \
https://auth.sandbox.messpunkt.io/oauth/authorize -o /dev/null \
| grep -i '^location:' | grep -oE 'code=[A-F0-9]+' | cut -d= -f2 | tr -d '\r\n')
echo "Got authorization code: $CODE"
2Exchange the code for a token
The authorization code is single-use and expires after 5 minutes. Redeem it at /oauth/token, presenting the PKCE verifier:
curl -s -X POST \
-d "grant_type=authorization_code" \
-d "code=$CODE" \
-d "client_id=sandbox-demo-client" \
-d "redirect_uri=http://localhost:3000/callback" \
-d "code_verifier=$VERIFIER" \
https://auth.sandbox.messpunkt.io/oauth/token > token.json
TOKEN=$(python3 -c "import json; print(json.load(open('token.json'))['access_token'])")
echo "Access token (first 30 chars): ${TOKEN:0:30}..."
The response contains an access_token (1 hour lifetime), a refresh_token (7 days, absolute session cap 30 days), and the granted scope. Save the refresh token for later — see Refreshing silently below.
3Identify yourself
Every ERP should call /v1/whoami after obtaining or refreshing a token to verify the tenant and Property scope:
curl -s -H "Authorization: Bearer $TOKEN" \
https://api.sandbox.messpunkt.io/v1/whoami | python3 -m json.tool
Returns:
{
"tenant_id": "3e17ba96-ae04-4d3b-9986-9e56f0adbf3a",
"tenant_name": "Musterhausverwaltung GmbH",
"client_id": "sandbox-demo-client",
"granted_scopes": ["read:units", "read:devices", "read:readings"],
"property_scope": ["6fb74d25-5517-4dcc-ae8b-0914579ab036"]
}
4Read consumption data
List the UsageUnits the landlord gave you access to, pick one, and request its warm-water consumption for a date range. The default sandbox data has a meter-replacement scenario on unit "WE 01" — you'll see two device segments in the response.
UNIT=$(curl -s -H "Authorization: Bearer $TOKEN" \
https://api.sandbox.messpunkt.io/v1/usage-units \
| python3 -c "import json,sys; print(json.load(sys.stdin)['data'][0]['id'])")
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.sandbox.messpunkt.io/v1/usage-units/$UNIT/consumption?from=2026-01-01&to=2026-12-31&metric=water_warm" \
| python3 -m json.tool
The response gives you, per measuring point, exactly two readings per device segment — start and end of the installation window, clipped to your query range. Compute consumption as readings[1].value - readings[0].value and sum across segments when a meter was replaced mid-range. See the Readings section in the Reference for the full contract.
Refreshing silently
When the 1-hour access token expires, exchange the refresh token for a fresh pair — no user interaction needed:
curl -s -X POST \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN" \
-d "client_id=sandbox-demo-client" \
https://auth.sandbox.messpunkt.io/oauth/token