SoundNativ Connector API

Let your app or AI assistant read a SoundNativ user's profile and pronunciation practice progress, with their permission.

Overview

  • REST API, JSON responses wrapped in a data envelope.
  • Read-only. No endpoint changes a user's account, and none expose email addresses, payment details or audio recordings.
  • Access is granted by the user through OAuth 2.0 (authorization code flow with PKCE). Users can disconnect your app at any time from their SoundNativ dashboard, which revokes its tokens immediately.
  • Works for both free and paid SoundNativ accounts.
  • Base URL: https://soundnativ.com/api/connector

Getting access

Clients are registered by the SoundNativ team. Email [email protected] with your app name, your exact redirect URIs (https, or http on localhost for development) and the scopes you need. You will receive a client ID and client secret. Keep the secret on your server; it is shown only once.

Scopes

  • profile:read: See your name, native language, daily goal and plan
  • progress:read: See your streak, sound mastery and score history

OAuth endpoints

  • Discovery (RFC 8414): https://soundnativ.com/.well-known/oauth-authorization-server
  • Authorization: https://soundnativ.com/oauth/authorize
  • Token: https://soundnativ.com/api/oauth/token
  • Revocation (RFC 7009): https://soundnativ.com/api/oauth/revoke
  • OpenAPI spec: https://soundnativ.com/api/connector/openapi.json

Supported: response_type=code, grant types authorization_code and refresh_token, PKCE method S256 (required), client authentication client_secret_basic or client_secret_post. Token and revocation requests must be application/x-www-form-urlencoded.

Authorization flow

1. Send the user to the authorization URL

Generate a random code_verifier (43 to 128 characters) and send its base64url SHA-256 as the code_challenge. The redirect_uri must exactly match one you registered.

https://soundnativ.com/oauth/authorize
  ?response_type=code
  &client_id=snc_...
  &redirect_uri=https%3A%2F%2Fyourapp.example%2Foauth%2Fcallback
  &scope=profile%3Aread%20progress%3Aread
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256

The user signs in to SoundNativ and sees a consent screen with your app's name and the requested scopes. If they allow access, they are redirected to your redirect_uri with code and state. If they cancel, you receive error=access_denied.

2. Exchange the code for tokens

The code is single use and expires after 2 minutes.

curl -X POST https://soundnativ.com/api/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://yourapp.example/oauth/callback \
  -d code_verifier="$CODE_VERIFIER"
{
  "access_token": "snoa_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "snor_...",
  "scope": "profile:read progress:read"
}

3. Call the API

curl https://soundnativ.com/api/connector/streak \
  -H "Authorization: Bearer $ACCESS_TOKEN"

4. Refresh before the access token expires

Access tokens last 1 hour. Refresh tokens last 60 days and the window restarts on every refresh, so an app in regular use stays connected.

curl -X POST https://soundnativ.com/api/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"

Refresh tokens rotate. Every refresh returns a new access token and a new refresh token, and the old refresh token stops working. Always store the new one. Presenting a refresh token that was already used is treated as a leak and revokes the connection, so make sure two workers never refresh with the same token at once. You may pass a narrower scope when refreshing, never a wider one.

Endpoints

All dates are YYYY-MM-DD in the user's timezone (UTC if unknown). Scores range from 0 to 100. New users have little or no progress data until they practice.

GET /profile

Scope: profile:read. Name, native language, daily practice goal (lessons per day), timezone and plan. Never includes email, ids or billing details.

{
  "data": {
    "name": "Ana",
    "nativeLanguage": "Spanish",
    "dailyGoal": 3,
    "timezone": "Europe/Madrid",
    "plan": "pro"
  }
}

GET /streak

Scope: progress:read. Current and longest streak, today's lessons against the daily goal, and the days with activity in the last 14 days. A streak still counts if the goal was last met today or yesterday.

{
  "data": {
    "dailyGoal": 3,
    "todayLessonsCompleted": 2,
    "dailyGoalMet": false,
    "activeStreak": 6,
    "currentStreak": 6,
    "longestStreak": 11,
    "lastStreakDate": "2026-10-04",
    "timezone": "Europe/Madrid",
    "calendar": [
      { "dateKey": "2026-10-04", "lessonsCompleted": 3, "goalHit": true },
      { "dateKey": "2026-10-05", "lessonsCompleted": 2, "goalHit": false }
    ]
  }
}

GET /sound-mastery

Scope: progress:read. One entry per English sound in the curriculum, highest score first. Sounds not practiced yet have a null score.

{
  "data": {
    "sounds": [
      { "sound": "th", "bestScore": 82.5, "stepsCompleted": 4, "totalSteps": 6 },
      { "sound": "r", "bestScore": null, "stepsCompleted": 0, "totalSteps": 5 }
    ]
  }
}

GET /score-history?days=30

Scope: progress:read. Average score of the lessons completed each day, oldest first, ending today. days is clamped to 1 to 90 (default 30). Days without lessons have a null score.

{
  "data": {
    "timezone": "Europe/Madrid",
    "days": 30,
    "overallScore": 76.4,
    "entries": [
      { "dateKey": "2026-09-06", "avgScore": null, "lessonsCompleted": 0 },
      { "dateKey": "2026-09-07", "avgScore": 71, "lessonsCompleted": 2 }
    ]
  }
}

Errors and limits

  • 401: the access token is missing, expired or revoked (WWW-Authenticate: Bearer error="invalid_token"). Refresh once and retry; if refreshing fails with invalid_grant, the user has disconnected your app and must authorize it again.
  • 403: the token lacks the endpoint's scope (error="insufficient_scope").
  • 429: more than 120 API requests per minute for one connected user, or more than 60 token requests per minute from one IP.
  • OAuth errors follow RFC 6749: { "error": "invalid_grant", "error_description": "..." }. API errors look like { "error": "Unauthorized" }.
  • Responses are sent with Cache-Control: no-store. Do not cache user data longer than you need it.

Revoking access

When a user disconnects SoundNativ inside your app, revoke the token so it no longer appears in their SoundNativ dashboard:

curl -X POST https://soundnativ.com/api/oauth/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"

Revoking either token ends the whole connection. The endpoint always returns 200.

Support

Questions or problems: [email protected]. See also our Privacy Policy and Terms of Service.