--- title: 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. ## Creating an OAuth app 1. Sign in to your Hackatime account 2. Go to [My OAuth Apps](https://hackatime.hackclub.com/oauth/applications) 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://sinerider.com/auth/callback`) - **Scopes** - the permissions your app needs (see [Scopes](#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**! ## Scopes Scopes control what data your app can access. It's a good idea to only request 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 | | `admin` | Access the [Admin API](#admin-api-access) on the authorizing user's behalf, subject to their role permissions. | 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 ``` ### Admin scope restrictions - Only **admin+** users (`admin`, `superadmin`, `ultraadmin`) can attach the `admin` scope to an OAuth application. - **Viewers and admin+ users** (`viewer`, `admin`, `superadmin`, `ultraadmin`) can authorize an approved application requesting the `admin` scope. The token does not grant permissions beyond the authorizing user's role. - Apps with the `admin` scope must be **confidential** (server-side clients that can keep a secret). - **This scope requires approval from Hack Club HQ to use.** It won't work if you try to use it without permission! ## 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 endpoint 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://sinerider.com/auth/callback&response_type=code&scope=profile+read&state=random_string ``` ### Step 2: Handle the callback After the user authorizes (or denies), they are redirected to your `redirect_uri` with a `code` parameter: ``` https://sinerider.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:** ```json { "access_token": "abc123...", "token_type": "Bearer", "expires_in": 504911232, "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. Unless, of course, you want your Hackatime app to keep working after 16 years... ### 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 ``` ### Admin API access With a token that includes the `admin` scope, call Admin API endpoints the same way (instead of minting an Admin API key): ``` GET https://hackatime.hackclub.com/api/admin/v1/check Authorization: Bearer YOUR_ACCESS_TOKEN ``` ## OAuth-authenticated API endpoints All endpoints below require a valid OAuth access token in the `Authorization: Bearer ` header. ### GET /api/v1/authenticated/me **Required scope:** `profile` Returns information about the authenticated user. **Response:** ```json { "id": 123, "emails": ["liam@sinerider.com"], "slack_id": "U059VC0UDEU", "github_username": "liamcal", "trust_factor": { "trust_level": "green", "trust_value": 2 } } ``` ### GET /api/v1/authenticated/hours **Required scope:** `read` 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:** ```json { "start_date": "2025-01-01", "end_date": "2025-01-07", "total_seconds": 36000 } ``` ### GET /api/v1/authenticated/streak **Required scope:** `read` Returns the user's current coding streak. **Response:** ```json { "streak_days": 14 } ``` ### GET /api/v1/authenticated/projects **Required scope:** `read` Returns the user's projects with time totals. **Query parameters:** | Parameter | Default | Description | |-----------|---------|-------------| | `include_archived` | `false` | Set to `true` to include archived projects | **Response:** ```json { "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 **Required scope:** `read` Returns the user's most recent heartbeat. **Response:** ```json { "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" } ``` If the user has no heartbeats (excluding setup test entries), the endpoint returns **200 OK** with: ```json { "heartbeat": null } ``` ### GET /api/v1/authenticated/api_keys This endpoint does not require a specific OAuth scope beyond a valid access token. Returns the user's Hackatime API key (creates one if none exists). **Response:** ```json { "token": "abc123..." } ``` ## Revoking access ### As a user Users can revoke access to your app at any time: 1. Go to [Privacy & Security](https://hackatime.hackclub.com/my/settings/privacy#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? - Check the [interactive API docs](https://hackatime.hackclub.com/api-docs) for full endpoint details. - Ask in [#hackatime-help](https://hackclub.slack.com/archives/C07MQ845X1F) on Slack. - [Open an issue](https://github.com/hackclub/hackatime/issues) on GitHub.