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

  1. Sign in to your Hackatime account
  2. Go to My OAuth Apps
  3. Click New Application
  4. 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.
  5. 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:

  1. Go to Authorized Applications
  2. 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:

  1. Generate a random code_verifier (43-128 characters, URL-safe)
  2. Create a code_challenge by computing BASE64URL(SHA256(code_verifier))
  3. Include code_challenge and code_challenge_method=S256 in the authorization URL
  4. Include code_verifier when 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?

Found an issue with this page? Edit on GitHub

Using Inertia. Build from about 4 hours ago. 200 heartbeats (0 imported) in the past 24 hours. (DB: 2 queries, 0 cached) (CACHE: 0 hits, 0 misses) (1.2 req/sec)