API reference
Request formats, response shapes, OAuth examples, and troubleshooting for tenant integrations with SoWayJuli.
Use your registered SoWayJuli HTTPS origin as BASE_URL. Never put authorization codes or tokens in a URL. Keep certificate validation enabled in API clients.
Response format
JSON endpoints return an envelope. Always check the HTTP status first, then inspect success and error.code.
{
"success": true,
"data": { "example": "value" },
"error": null,
"requestId": "request-correlation-id"
}
{
"success": false,
"data": null,
"error": { "code": "INVALID_GRANT", "message": "invalid_grant" },
"requestId": "request-correlation-id"
}
Website account APIs
These endpoints support SoWayJuli's own website and account-management flows. Tenant applications and third-party services must use the OAuth endpoints in this reference instead.
GET /api/v0/backend-api/PasswordEncryptionKeysupports the website's password forms.POST /api/v0/backend-api/Registercreates a SoWayJuli user account when registration is enabled.POST /api/v0/backend-api/Loginestablishes the website's browser session.GET /api/v0/backend-api/Mereturns the current website account.POST /api/v0/backend-api/Logoutends the website's browser session.POST /api/v0/backend-api/UpdateProfile,POST /api/v0/backend-api/ChangeEmail, andPOST /api/v0/backend-api/ChangePasswordmanage the signed-in website account.
OAuth authorization
POST /api/v0/backend-api/graph/authorize
Begins an authorization transaction. The tenant_id, client_id, software_id, and callback must match an active application registration. scope is a space-separated list and defaults to profile. Generate a random state per attempt and validate it at the callback. Use PKCE with S256.
curl -X POST "$BASE_URL/api/v0/backend-api/graph/authorize" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"tenant_id":"TENANT_ID",
"client_id":"CLIENT_ID",
"software_id":"SOFTWARE_ID",
"callback":"http://localhost:8080/callback",
"scope":"profile email",
"state":"RANDOM_STATE",
"code_challenge":"BASE64URL_SHA256_OF_VERIFIER",
"code_challenge_method":"S256"
}'
On HTTP success, inspect data.transaction_status. A pending transaction returns a data.login_url; open it in the user's browser. A response can be HTTP 200 while the transaction status is Failed, so do not treat HTTP 200 alone as authorization success.
POST /api/v0/backend-api/token
Exchange an authorization code. Codes expire quickly and can only be used once. The callback and client ID must match the authorization request.
curl -X POST "$BASE_URL/api/v0/backend-api/token" \
-H "Content-Type: application/json" \
-d '{
"grant_type":"authorization_code",
"code":"CALLBACK_CODE",
"callback":"http://localhost:8080/callback",
"client_id":"CLIENT_ID",
"code_verifier":"ORIGINAL_PKCE_VERIFIER"
}'
The response data contains access_token, token_type (Bearer), expires_in in seconds, refresh_token, and granted scope.
Refresh a token
curl -X POST "$BASE_URL/api/v0/backend-api/token" \
-H "Content-Type: application/json" \
-d '{"grant_type":"refresh_token","client_id":"CLIENT_ID","refresh_token":"CURRENT_REFRESH_TOKEN"}'
Refresh tokens rotate: store the newly returned refresh token and discard the old one. Reusing the old token returns invalid_grant.
Profile and media
GET /user/profile
Requires an access token in the Authorization header. The profile scope grants identity/profile fields, email grants the email field, and phone grants the phone number.
curl "$BASE_URL/user/profile" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Accept: application/json"
Fields outside the granted scopes contain NOT ALLOWED CURRENT SCOPE; this is a scope result, not a missing database value.
GET /usercdn/user/profile/avatarandGET /usercdn/user/profile/bannerreturn image bytes and require a bearer token withprofile.POST /api/v0/backend-api/UploadAvatarandPOST /api/v0/backend-api/UploadBanneraccept one multipart image file, requireprofile, and reject files at or above 64 MiB.
POST /user/logout
Tenant integrations may use this endpoint to revoke the bearer access token presented in the request. It does not sign out the user's SoWayJuli browser session or revoke other tokens.
curl -X POST "$BASE_URL/user/logout" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Accept: application/json"
A successful response returns data.logged_out: true and data.token_revoked: true. Missing, invalid, expired, or already revoked tokens return 401 Unauthorized.
Troubleshooting
AUTHORIZE_UNABLE_TO_CONNECT
Verify the tenant, client, and software IDs against the application registration. Check the callback character-for-character and request only scopes enabled for that application. Start a new authorization attempt after correcting the request.
invalid_request from token exchange
For authorization_code, include code, callback, and client_id. For refresh_token, include refresh_token and client_id.
invalid_grant
The code/token may be expired, already used/revoked, issued to a different client, or paired with a different callback. For PKCE, send the original verifier that matches the S256 challenge. Restart authorization for a fresh code.
401 Unauthorized on profile or media
Send Authorization: Bearer ACCESS_TOKEN. Access tokens expire; refresh the token and replace the stored access and refresh tokens with the newly issued pair.
403 INSUFFICIENT_SCOPE or NOT ALLOWED CURRENT SCOPE
Request the required scope in the app registration, authorize again, and use the newly issued token. Existing tokens do not gain newly configured scopes.
404 on a callback
Ensure the local callback listener is running, the callback path matches, and the full callback URI is registered exactly. Check the callback handler's path before parsing query values.
429 RATE_LIMIT_EXCEEDED
Wait for the duration in Retry-After before retrying. Use bounded backoff and avoid retrying non-idempotent requests automatically.
413 UPLOAD_TOO_LARGE or INVALID_IMAGE
Send one supported, decodable image smaller than 64 MiB. Check both the actual file bytes and the multipart field name expected by the client.
Unexpected 500
Retry once after a short delay. If the issue continues, contact the service operator with the endpoint, approximate time, and response requestId. Do not include passwords, tokens, or authorization codes.