OAuth Apps
Build integrations with Hackatime using OAuth 2.0. Create an OAuth app to let users authorize your application to access their Hackatime data.
Overview
Hackatime uses OAuth 2.0 (powered by Doorkeeper) to let third-party applications access user data on their behalf. This is the recommended way to build integrations -- users authorize your app through a consent screen and you receive an access token to make API requests.
Creating an OAuth App
- Sign in to your Hackatime account
- Go to My OAuth Apps
- Click New Application
- Fill in the form:
- Name -- a human-readable name for your app (shown on the consent screen)
- Redirect URIs -- one URI per line where users are sent after authorizing (e.g.
https://orpheus.gg/auth/callback) - Scopes -- the permissions your app needs (see Scopes below)
- Confidential -- check this if your app can keep a client secret safe (server-side apps). Leave unchecked for native/mobile/SPA apps.
- Click Submit
After creation you will see your Client ID (UID) and Client Secret. Store the secret securely -- you won't be able to view it again.
Scopes
Scopes control what data your app can access. Request only the scopes you need.
| Scope | Description | Granted by Default |
|---|---|---|
profile |
Access basic profile information (user ID, email addresses, Slack ID, GitHub username, trust factor) | Yes |
read |
View basic info about the user's Hackatime account | No |
If you don't specify any scopes, only the profile scope is granted.
When requesting scopes in the authorization URL, separate multiple scopes with spaces:
scope=profile+read
Authorization Flow
Hackatime supports the standard Authorization Code flow. PKCE (Proof Key for Code Exchange) is also supported for public clients.
Step 1: Redirect Users to Authorize
Send users to the authorization endpoint:
GET https://hackatime.hackclub.com/oauth/authorize
Parameters:
| Parameter | Required | Description |
|---|---|---|
client_id |
Yes | Your app's UID |
redirect_uri |
Yes | Must match one of your registered redirect URIs |
response_type |
Yes | Set to code |
scope |
No | Space-separated list of scopes (defaults to profile) |
state |
Recommended | A random string to prevent CSRF attacks. You should verify this matches when the user is redirected back. |
code_challenge |
For PKCE | The code challenge for PKCE |
code_challenge_method |
For PKCE | The method used to generate the challenge (e.g. S256) |
Example:
https://hackatime.hackclub.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://example.com/auth/callback&response_type=code&scope=profile+read&state=random_string
The user sees a consent screen showing your app name and the permissions you are requesting. If your app is not verified, a warning is displayed to the user.
Step 2: Handle the Callback
After the user authorizes (or denies), they are redirected to your redirect_uri with a code parameter:
https://example.com/auth/callback?code=AUTHORIZATION_CODE&state=random_string
If the user denies authorization, the callback includes an error parameter instead.
Step 3: Exchange the Code for a Token
Make a POST request to the token endpoint to exchange the authorization code for an access token:
POST https://hackatime.hackclub.com/oauth/token
Parameters (form-encoded body):
| Parameter | Required | Description |
|---|---|---|
client_id |
Yes | Your app's UID |
client_secret |
Yes (confidential apps) | Your app's secret |
code |
Yes | The authorization code from the callback |
redirect_uri |
Yes | Must match the URI used in the authorization request |
grant_type |
Yes | Set to authorization_code |
code_verifier |
For PKCE | The original code verifier if you used PKCE |
Example response:
{
"access_token": "abc123...",
"token_type": "Bearer",
"expires_in": 504576000,
"scope": "profile read",
"created_at": 1700000000
}
Access tokens are long-lived (approximately 16 years) so you typically don't need to worry about refreshing them.
Step 4: Make API Requests
Use the access token in the Authorization header:
GET https://hackatime.hackclub.com/api/v1/authenticated/me
Authorization: Bearer YOUR_ACCESS_TOKEN
OAuth-Authenticated API Endpoints
All endpoints below require a valid OAuth access token in the Authorization: Bearer <token> header.
GET /api/v1/authenticated/me
Returns information about the authenticated user.
Response:
{
"id": 123,
"emails": ["user@example.com"],
"slack_id": "U01234ABC",
"github_username": "octocat",
"trust_factor": {
"trust_level": "green",
"trust_value": 2
}
}
GET /api/v1/authenticated/hours
Returns total coding time for a date range.
Query parameters:
| Parameter | Default | Description |
|---|---|---|
start_date |
7 days ago | Start date (YYYY-MM-DD) |
end_date |
Today | End date (YYYY-MM-DD) |
Response:
{
"start_date": "2025-01-01",
"end_date": "2025-01-07",
"total_seconds": 36000
}
GET /api/v1/authenticated/streak
Returns the user's current coding streak.
Response:
{
"streak_days": 14
}
GET /api/v1/authenticated/projects
Returns the user's projects with time totals.
Query parameters:
| Parameter | Default | Description |
|---|---|---|
include_archived |
false |
Set to true to include archived projects |
Response:
{
"projects": [
{
"name": "my-project",
"total_seconds": 72000,
"most_recent_heartbeat": "2025-01-07T15:30:00Z",
"languages": ["Ruby", "JavaScript"],
"archived": false
}
]
}
GET /api/v1/authenticated/heartbeats/latest
Returns the user's most recent heartbeat.
Response:
{
"id": 456,
"created_at": "2025-01-07T15:30:00Z",
"time": 1736264400.0,
"category": "coding",
"project": "my-project",
"language": "Ruby",
"editor": "VS Code",
"operating_system": "Mac",
"machine": "MacBook-Pro",
"entity": "app/models/user.rb"
}
GET /api/v1/authenticated/api_keys
Returns the user's Hackatime API key (creates one if none exists).
Response:
{
"token": "abc123..."
}
Revoking Access
As a User
Users can revoke access to your app at any time:
- Go to Authorized Applications
- Click Revoke next to the app
Programmatically
You can revoke a token by calling the revoke endpoint:
POST https://hackatime.hackclub.com/oauth/revoke
Parameters:
| Parameter | Required | Description |
|---|---|---|
token |
Yes | The access token to revoke |
client_id |
Yes | Your app's UID |
client_secret |
Yes (confidential apps) | Your app's secret |
PKCE for Public Clients
If your app cannot securely store a client secret (mobile apps, desktop apps, SPAs), use PKCE:
- Generate a random
code_verifier(43-128 characters, URL-safe) - Create a
code_challengeby computingBASE64URL(SHA256(code_verifier)) - Include
code_challengeandcode_challenge_method=S256in the authorization URL - Include
code_verifierwhen exchanging the code for a token
You can also leave the Confidential checkbox unchecked and omit the client_secret in your token requests.
App Verification
New OAuth apps are marked as unverified. Unverified apps trigger a warning on the consent screen telling users the app has not been reviewed. To get your app verified, shoot Mahad a DM on the Slack! Verified apps:
- Don't show an "unverified" warning during authorization
- Have their name locked to prevent impersonation (only admins can rename them)
Need help?
- Check the interactive API docs for full endpoint details.
- Ask in #hackatime-help on Slack.
- Open an issue on GitHub.