OAuth 2.0 is a secure authorization protocol that allows third-party applications to access your HytaleMetrics data without sharing your password. When you authorize an application, it receives a token that's tied to a specific server you select.
Step 1: Create a Developer Application
1. Navigate to Developer Portal in your dashboard
2. Click "Create New Application"
3. Enter your application name and callback URL
4. Copy your Client ID and Client Secret - the secret is only shown once!
The OAuth flow follows these steps (click each to expand):
Create a code verifier and challenge for enhanced security:
// Generate random code verifier (43-128 characters)
$codeVerifier = bin2hex(random_bytes(32));
// Create SHA256 hash and base64 encode
$codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');
Send the user to the authorization endpoint:
https://hytalemetrics.net/oauth/authorize?
client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=read:server write:events read:players read:analytics
&state=RANDOM_STATE_STRING
&code_challenge=CODE_CHALLENGE
&code_challenge_method=S256
The user will:
User is redirected back to your callback URL with a code:
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE_STRING
Make a POST request to exchange the code for tokens:
POST https://hytalemetrics.net/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &client_id=YOUR_CLIENT_ID &client_secret=YOUR_CLIENT_SECRET &redirect_uri=https://yourapp.com/callback &code=AUTHORIZATION_CODE &code_verifier=CODE_VERIFIER
Response:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh_token": "def50200a8c5b..."
}
Request only the scopes your application needs:
read:server - Read server information and status
write:events - Submit gameplay events and metrics
read:players - Access player data and statistics
read:analytics - View analytics and reports
All API requests must include your access token in the Authorization header:
Authorization: Bearer YOUR_ACCESS_TOKEN
GET /api/v2/example - Test endpoint with example data
Scope: read:server
curl -X GET https://hytalemetrics.net/api/v2/example \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Accept: application/json"
GET /api/v2/status - Get server status
Scope: read:server
{
"success": true,
"server": {
"id": 123,
"name": "My Server",
"game": "Hytale",
"game_type": "survival",
"status": "online"
}
}
POST /api/v2/event - Submit gameplay events
Scope: write:events
curl -X POST https://hytalemetrics.net/api/v2/event \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"event_type": "player_join",
"player": "PlayerName",
"timestamp": "2026-01-18 12:00:00"
}
]
}'
GET /api/v2/players - Get player data
Scope: read:players
{
"success": true,
"players": {
"online": 42,
"status": "online"
}
}
GET /api/v2/analytics - Get analytics data
Scope: read:analytics
{
"success": true,
"analytics": {
"server_name": "My Server",
"period": "last_7_days",
"metrics": [...]
}
}
Select your language to view implementation examples:
<?php
// Step 1: Generate PKCE
$codeVerifier = bin2hex(random_bytes(32));
$codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');
$state = bin2hex(random_bytes(16));
$_SESSION['code_verifier'] = $codeVerifier;
$_SESSION['oauth_state'] = $state;
// Step 2: Redirect to authorization
$authUrl = 'https://hytalemetrics.net/oauth/authorize?' . http_build_query([
'client_id' => 'YOUR_CLIENT_ID',
'redirect_uri' => 'https://yourapp.com/callback',
'response_type' => 'code',
'scope' => 'read:server write:events',
'state' => $state,
'code_challenge' => $codeChallenge,
'code_challenge_method' => 'S256',
]);
header('Location: ' . $authUrl);
// Step 3: Handle callback
if ($_GET['state'] !== $_SESSION['oauth_state']) {
die('Invalid state');
}
// Step 4: Exchange code for token
$ch = curl_init('https://hytalemetrics.net/oauth/token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'grant_type' => 'authorization_code',
'client_id' => 'YOUR_CLIENT_ID',
'client_secret' => 'YOUR_CLIENT_SECRET',
'redirect_uri' => 'https://yourapp.com/callback',
'code' => $_GET['code'],
'code_verifier' => $_SESSION['code_verifier'],
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$tokens = json_decode(curl_exec($ch), true);
// Step 5: Use the access token
$ch = curl_init('https://hytalemetrics.net/api/v2/status');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $tokens['access_token'],
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$serverData = json_decode(curl_exec($ch), true);
?>
const crypto = require('crypto');
const axios = require('axios');
// Step 1: Generate PKCE
const codeVerifier = crypto.randomBytes(32).toString('hex');
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');
const state = crypto.randomBytes(16).toString('hex');
// Step 2: Redirect to authorization
const authUrl = `https://hytalemetrics.net/oauth/authorize?` + new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
redirect_uri: 'https://yourapp.com/callback',
response_type: 'code',
scope: 'read:server write:events',
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});
// res.redirect(authUrl);
// Step 3: Handle callback
app.get('/callback', async (req, res) => {
if (req.query.state !== state) {
return res.status(400).send('Invalid state');
}
// Step 4: Exchange code for token
const tokenResponse = await axios.post('https://hytalemetrics.net/oauth/token', {
grant_type: 'authorization_code',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uri: 'https://yourapp.com/callback',
code: req.query.code,
code_verifier: codeVerifier,
});
const { access_token } = tokenResponse.data;
// Step 5: Use the access token
const serverData = await axios.get('https://hytalemetrics.net/api/v2/status', {
headers: {
Authorization: `Bearer ${access_token}`,
},
});
res.json(serverData.data);
});
import hashlib
import secrets
import base64
import requests
# Step 1: Generate PKCE
code_verifier = secrets.token_urlsafe(32)
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).decode().rstrip('=')
state = secrets.token_hex(16)
# Step 2: Redirect to authorization
auth_url = f"https://hytalemetrics.net/oauth/authorize?" + "&".join([
f"client_id=YOUR_CLIENT_ID",
f"redirect_uri=https://yourapp.com/callback",
f"response_type=code",
f"scope=read:server write:events",
f"state={state}",
f"code_challenge={code_challenge}",
f"code_challenge_method=S256",
])
# return redirect(auth_url)
# Step 3: Handle callback
@app.route('/callback')
def callback():
if request.args.get('state') != state:
return 'Invalid state', 400
# Step 4: Exchange code for token
token_response = requests.post('https://hytalemetrics.net/oauth/token', data={
'grant_type': 'authorization_code',
'client_id': 'YOUR_CLIENT_ID',
'client_secret': 'YOUR_CLIENT_SECRET',
'redirect_uri': 'https://yourapp.com/callback',
'code': request.args.get('code'),
'code_verifier': code_verifier,
})
tokens = token_response.json()
# Step 5: Use the access token
server_data = requests.get('https://hytalemetrics.net/api/v2/status', headers={
'Authorization': f"Bearer {tokens['access_token']}",
})
return server_data.json()
import java.net.http.*;
import java.security.*;
import java.util.*;
public class OAuthClient {
public static void main(String[] args) throws Exception {
// Step 1: Generate PKCE
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[32];
random.nextBytes(bytes);
String codeVerifier = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(codeVerifier.getBytes());
String codeChallenge = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(hash);
String state = UUID.randomUUID().toString();
// Step 2: Build authorization URL
String authUrl = "https://hytalemetrics.net/oauth/authorize?" +
"client_id=YOUR_CLIENT_ID&" +
"redirect_uri=https://yourapp.com/callback&" +
"response_type=code&" +
"scope=read:server+write:events&" +
"state=" + state + "&" +
"code_challenge=" + codeChallenge + "&" +
"code_challenge_method=S256";
// Step 4: Exchange code for token
HttpClient client = HttpClient.newHttpClient();
String tokenBody = "grant_type=authorization_code&" +
"client_id=YOUR_CLIENT_ID&" +
"client_secret=YOUR_CLIENT_SECRET&" +
"redirect_uri=https://yourapp.com/callback&" +
"code=AUTHORIZATION_CODE&" +
"code_verifier=" + codeVerifier;
HttpRequest tokenRequest = HttpRequest.newBuilder()
.uri(URI.create("https://hytalemetrics.net/oauth/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(tokenBody))
.build();
HttpResponse<String> tokenResponse = client.send(
tokenRequest,
HttpResponse.BodyHandlers.ofString()
);
// Step 5: Use access token
HttpRequest apiRequest = HttpRequest.newBuilder()
.uri(URI.create("https://hytalemetrics.net/api/v2/status"))
.header("Authorization", "Bearer ACCESS_TOKEN")
.GET()
.build();
HttpResponse<String> apiResponse = client.send(
apiRequest,
HttpResponse.BodyHandlers.ofString()
);
System.out.println(apiResponse.body());
}
}
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"net/http"
"net/url"
)
func main() {
// Step 1: Generate PKCE
verifier := make([]byte, 32)
rand.Read(verifier)
codeVerifier := hex.EncodeToString(verifier)
hash := sha256.Sum256([]byte(codeVerifier))
codeChallenge := base64.URLEncoding.WithPadding(base64.NoPadding).
EncodeToString(hash[:])
stateBytes := make([]byte, 16)
rand.Read(stateBytes)
state := hex.EncodeToString(stateBytes)
// Step 2: Build authorization URL
authUrl := "https://hytalemetrics.net/oauth/authorize?" +
url.Values{
"client_id": {"YOUR_CLIENT_ID"},
"redirect_uri": {"https://yourapp.com/callback"},
"response_type": {"code"},
"scope": {"read:server write:events"},
"state": {state},
"code_challenge": {codeChallenge},
"code_challenge_method": {"S256"},
}.Encode()
// Step 4: Exchange code for token
tokenResp, _ := http.PostForm("https://hytalemetrics.net/oauth/token",
url.Values{
"grant_type": {"authorization_code"},
"client_id": {"YOUR_CLIENT_ID"},
"client_secret": {"YOUR_CLIENT_SECRET"},
"redirect_uri": {"https://yourapp.com/callback"},
"code": {"AUTHORIZATION_CODE"},
"code_verifier": {codeVerifier},
})
var tokens map[string]interface{}
json.NewDecoder(tokenResp.Body).Decode(&tokens)
// Step 5: Use access token
req, _ := http.NewRequest("GET", "https://hytalemetrics.net/api/v2/status", nil)
req.Header.Set("Authorization", "Bearer "+tokens["access_token"].(string))
client := &http.Client{}
resp, _ := client.Do(req)
var serverData map[string]interface{}
json.NewDecoder(resp.Body).Decode(&serverData)
}
Use the built-in OAuth Test Console to test your OAuth flow without writing code. It simulates a third-party application and shows you each step of the process.
- Never share your client secret publicly
- Always use HTTPS for your callback URLs
- Validate the state parameter to prevent CSRF attacks
- Use PKCE (code challenge/verifier) for enhanced security
- Store access tokens securely
- Request only the scopes you need
- Tokens expire after 1 year - use refresh tokens to get new ones
API endpoints are rate limited to 100 requests per minute per access token. If you exceed this limit, you'll receive a 429 Too Many Requests response.
- View your applications: Developer Portal
- Test OAuth flow: OAuth Test Console
- Check server status: Dashboard