Welcome to the Perminut Developer API documentation. This guide will help you integrate your own extensions, Add-ons, and third-party services with our platform.
Building extensions is easy! You can use our secure REST API to manage time entries, projects, and retrieve user information.
Most endpoints require an active session. If you are building a browser extension, authentication is natively handled via cookies once the user logs in through the dashboard. Secure cross-origin requests require the inclusion of credentials (e.g., credentials: 'include' in fetch).
Security Note: Always ensure your requests are sent over HTTPS. The API will reject unsecured connections.
Retrieves the authenticated user's profile, role, and organization details. Returns a 401 Unauthorized status if the user is not logged in.
curl -X GET https://api.perminut.com/api/auth/me \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
fetch('https://api.perminut.com/api/auth/me', { method: 'GET', credentials: 'include' }) .then(res => { if (!res.ok) throw new Error('Not authenticated'); return res.json(); }) .then(data => console.log('Current User:', data)) .catch(err => console.error(err));
Returns a list of all active projects for the user's organization. Useful for populating dropdowns in your custom time-tracking interfaces.
curl -X GET 'https://api.perminut.com/api/projects?limit=50&offset=0' \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \ -H "Accept: application/json"
fetch('https://api.perminut.com/api/projects?limit=50&offset=0', { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }) .then(res => res.json()) .then(projects => console.log('Available Projects:', projects));
Retrieves a list of recent time entries for the authenticated user, including any currently running timer.
curl -X GET 'https://api.perminut.com/api/time-entries?startDate=2026-03-10&endDate=2026-03-17' \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \ -H "Accept: application/json"
fetch('https://api.perminut.com/api/time-entries?startDate=2026-03-10&endDate=2026-03-17', { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }) .then(res => res.json()) .then(entries => console.log('Time Entries:', entries));
Start a new time entry or log a completed time block. If a timer is already running, it is recommended to stop it before starting a new one, though the system will automatically handle overlaps based on your organization's settings.
Parameters:
projectId (integer, required): The ID of the project to track time against.description (string, optional): What you are working on.startTime (string, optional): ISO-8601 formatted start time. Defaults to now.endTime (string, optional): If provided, creates a completed entry instead of a running timer.curl -X POST https://api.perminut.com/api/time-entries \ -H "Content-Type: application/json" \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \ -d '{ "projectId": 12, "description": "API Documentation Work", "startTime": "2026-03-17T10:00:00.000Z" }'
fetch('https://api.perminut.com/api/time-entries', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: 12, description: "API Documentation Work" }) }) .then(res => res.json()) .then(entry => console.log('Timer Started:', entry));
Stops a currently running time entry.
curl -X PUT https://api.perminut.com/api/time-entries/452/stop \ -H "Content-Type: application/json" \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \ -d '{ "endTime": "2026-03-17T11:45:00.000Z" }'
const entryId = 452; fetch(`https://api.perminut.com/api/time-entries/${entryId}/stop`, { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ endTime: new Date().toISOString() // Optional }) }) .then(res => res.json()) .then(entry => console.log('Timer Stopped:', entry));
Updates an existing time entry (e.g., changing the description or project). Ensure you only pass the fields you intend to update.
curl -X PUT https://api.perminut.com/api/time-entries/452 \ -H "Content-Type: application/json" \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \ -d '{ "projectId": 15, "description": "Updated description for time entry" }'
fetch('https://api.perminut.com/api/time-entries/452', { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectId: 15, description: "Updated description for time entry" }) }) .then(res => res.json()) .then(entry => console.log('Entry Updated:', entry));
Deletes a time entry permanently. Returns status 204 No Content on success.
curl -X DELETE https://api.perminut.com/api/time-entries/452 \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
fetch('https://api.perminut.com/api/time-entries/452', { method: 'DELETE', credentials: 'include' }) .then(res => { if(res.ok) console.log('Entry Deleted successfully'); });
Retrieves time tracking statistics for the user. Valid ranges: daily, weekly, monthly.
curl -X GET 'https://api.perminut.com/api/dashboard/stats/weekly?timezone=Europe/Istanbul' \ -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \ -H "Accept: application/json"
fetch('https://api.perminut.com/api/dashboard/stats/weekly?timezone=Europe/Istanbul', { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }) .then(res => res.json()) .then(stats => console.log('Weekly Stats:', stats));
The API utilizes standard HTTP status codes:
200 OK - Request successful.201 Created - Resource successfully created.400 Bad Request - Validation error. Check the response body for details.401 Unauthorized - User is not logged in or session expired.403 Forbidden - User lacks permissions for the requested action.404 Not Found - The requested resource does not exist.500 Server Error - Something went wrong on our end.For custom integrations (like sending alerts to internal systems), Perminut supports outgoing webhooks. You can configure webhooks in the admin dashboard to trigger on events like time_entry.created or ruleset.triggered.