Developer API

Last Updated: March 17, 2026

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.

Authentication

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.

GET /api/auth/me

Retrieves the authenticated user's profile, role, and organization details. Returns a 401 Unauthorized status if the user is not logged in.

cURL
curl -X GET https://api.perminut.com/api/auth/me \
  -H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
JavaScript API
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));

Projects

GET /api/projects

Returns a list of all active projects for the user's organization. Useful for populating dropdowns in your custom time-tracking interfaces.

cURL
curl -X GET 'https://api.perminut.com/api/projects?limit=50&offset=0' \
  -H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \
  -H "Accept: application/json"
JavaScript API
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));

Time Entries

GET /api/time-entries

Retrieves a list of recent time entries for the authenticated user, including any currently running timer.

cURL
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"
JavaScript API
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));

POST /api/time-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
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"
  }'
JavaScript API
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));

PUT /api/time-entries/:id/stop

Stops a currently running time entry.

cURL
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"
  }'
JavaScript API
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));

PUT /api/time-entries/:id

Updates an existing time entry (e.g., changing the description or project). Ensure you only pass the fields you intend to update.

cURL
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"
  }'
JavaScript API
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));

DELETE /api/time-entries/:id

Deletes a time entry permanently. Returns status 204 No Content on success.

cURL
curl -X DELETE https://api.perminut.com/api/time-entries/452 \
  -H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
JavaScript API
fetch('https://api.perminut.com/api/time-entries/452', {
  method: 'DELETE',
  credentials: 'include'
})
  .then(res => {
    if(res.ok) console.log('Entry Deleted successfully');
  });

Dashboard Statistics

GET /api/dashboard/stats/:range

Retrieves time tracking statistics for the user. Valid ranges: daily, weekly, monthly.

cURL
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"
JavaScript API
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));

Error Handling

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.

Webhooks (Enterprise)

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.