# How to Integrate Replit with Toggl

- Tool: Replit
- Difficulty: Intermediate
- Time required: 25 minutes
- Last updated: March 2026

## TL;DR

To integrate Replit with Toggl Track, generate a Toggl API token from your profile settings, store it in Replit Secrets (lock icon 🔒), and call the Toggl Track API v9 from Python or Node.js server-side code to create time entries, fetch project data, generate detailed reports, and build custom time tracking workflows. Use Autoscale for web apps or Reserved VM for scheduled reporting jobs.

## Why Connect Replit to Toggl Track?

Toggl Track is one of the most widely used time tracking tools among freelancers, agencies, and remote teams — with over 5 million users and an API that covers the full spectrum of time tracking workflows. Connecting your Replit app to Toggl enables you to automate time entry creation from external project management tools, build custom reporting dashboards that pull billable hours for client invoicing, sync time data with accounting software, and create internal tools that eliminate the need for your team to manually switch to the Toggl interface for every time entry.

The Toggl ecosystem consists of two separate APIs: the main Toggl Track API (v9) for creating and managing time entries, projects, clients, and workspaces, and the Toggl Reports API (v3) for pre-aggregated analytics across different time periods. Both use the same API token authentication. The Reports API is particularly powerful for generating client billing summaries and team productivity reports without building your own aggregation logic.

Replit's Secrets system (lock icon 🔒 in the sidebar) keeps your Toggl API token secure. The token has the same access level as your Toggl account — it can read and write all workspace data. Store it as TOGGL_API_TOKEN in Replit Secrets and access it with os.environ['TOGGL_API_TOKEN'] in Python or process.env.TOGGL_API_TOKEN in Node.js. The Toggl API uses unusual Basic Auth where the token is the username and the literal string 'api_token' is the password — this is a Toggl-specific convention.

## Before you start

- A Replit account with a Python or Node.js project created
- A Toggl Track account (free tier available with full API access)
- At least one workspace and one project created in Toggl Track
- Basic familiarity with REST APIs and HTTP Basic Authentication
- Node.js 18+ or Python 3.10+ (both available on Replit by default)

## Step-by-step guide

### 1. Get Your Toggl API Token and Workspace ID

Log in to your Toggl Track account at track.toggl.com. Click your avatar or name in the bottom-left corner of the sidebar and select 'Profile settings'. Scroll to the very bottom of the Profile Settings page to find the 'API Token' section. Your API token is displayed there — it is a 32-character hexadecimal string. Click 'Click to reveal' if it is hidden, then copy it.

To find your Workspace ID, click on the workspace name in the Toggl sidebar (usually 'My Workspace' or your company name). The Workspace ID appears in the URL as a number: track.toggl.com/1234567/. Alternatively, call the Toggl API with a test request and read the ID from the response.

Toggl uses a specific Basic Auth convention: the username is your API token, and the password is the literal string 'api_token'. This is different from most APIs. Both the main API (api.track.toggl.com/api/v9/) and the Reports API (reports.track.toggl.com/api/v3/) use this same authentication pattern.

If you work across multiple workspaces (for example, your personal workspace and a client workspace), note both workspace IDs — you will specify which workspace to use in each API request.

**Expected result:** You have your 32-character Toggl API token and your numeric Workspace ID copied and ready to store in Replit Secrets.

### 2. Store Toggl Credentials in Replit Secrets

Open your Replit project and click the lock icon 🔒 in the left sidebar to open the Secrets pane. Add the following secrets:

- Key: TOGGL_API_TOKEN — Value: your 32-character API token
- Key: TOGGL_WORKSPACE_ID — Value: your numeric Workspace ID

Click 'Add Secret' after each one. Replit encrypts these values and injects them as environment variables at runtime. In Python, access them with os.environ['TOGGL_API_TOKEN']. In Node.js, use process.env.TOGGL_API_TOKEN.

The Toggl API uses Basic Auth where the username is the API token and the password is literally 'api_token'. In Python with the requests library, pass auth=(os.environ['TOGGL_API_TOKEN'], 'api_token'). In Node.js with axios, set auth: { username: process.env.TOGGL_API_TOKEN, password: 'api_token' }. Do not confuse this with APIs where the password field is the API secret — for Toggl, the string 'api_token' is always the password.

**Expected result:** Two Secrets (TOGGL_API_TOKEN and TOGGL_WORKSPACE_ID) appear in the Replit Secrets pane with masked values.

### 3. Manage Time Entries with Python

The Toggl Track API v9 is a REST API at https://api.track.toggl.com/api/v9/. Authentication uses HTTP Basic Auth with your API token as the username and 'api_token' as the password (literally — this is Toggl's unconventional but consistent auth pattern).

Time entries in Toggl require a description (can be empty), a workspace ID, and a start time in ISO 8601 format with timezone offset. For completed entries, also provide a duration in seconds (positive number). For in-progress timers, use a duration of -1 * (Unix timestamp of start time) — Toggl calculates the running duration on the fly.

The code below provides a complete Python client including: listing projects and workspaces, starting and stopping timers, creating completed time entries, and fetching recent entries. All timestamps must include timezone information — Toggl rejects naive datetimes.

```
import os
import requests
from datetime import datetime, timezone, timedelta

API_TOKEN = os.environ["TOGGL_API_TOKEN"]
WORKSPACE_ID = int(os.environ["TOGGL_WORKSPACE_ID"])

BASE_URL = "https://api.track.toggl.com/api/v9"

# Toggl uses Basic Auth: API token as username, 'api_token' as password (literal string)
AUTH = (API_TOKEN, "api_token")
HEADERS = {"Content-Type": "application/json"}

def get_me() -> dict:
    """Get current user info and default workspace ID."""
    response = requests.get(f"{BASE_URL}/me", auth=AUTH)
    response.raise_for_status()
    return response.json()

def get_workspaces() -> list:
    """List all workspaces."""
    response = requests.get(f"{BASE_URL}/workspaces", auth=AUTH)
    response.raise_for_status()
    return response.json()

def get_projects() -> list:
    """List all projects in the workspace."""
    url = f"{BASE_URL}/workspaces/{WORKSPACE_ID}/projects"
    response = requests.get(url, auth=AUTH, params={"active": "true"})
    response.raise_for_status()
    return response.json() or []

def get_clients() -> list:
    """List all clients in the workspace."""
    url = f"{BASE_URL}/workspaces/{WORKSPACE_ID}/clients"
    response = requests.get(url, auth=AUTH)
    response.raise_for_status()
    return response.json() or []

def create_time_entry(
    description: str,
    start: datetime,
    end: datetime,
    project_id: int = None,
    billable: bool = False,
    tags: list = None
) -> dict:
    """
    Create a completed time entry.
    duration is calculated automatically from start and end.
    start and end must be timezone-aware datetime objects.
    """
    duration_seconds = int((end - start).total_seconds())
    data = {
        "description": description,
        "start": start.isoformat(),
        "duration": duration_seconds,
        "workspace_id": WORKSPACE_ID,
        "created_with": "replit-integration",
        "billable": billable
    }
    if project_id:
        data["project_id"] = project_id
    if tags:
        data["tags"] = tags

    url = f"{BASE_URL}/workspaces/{WORKSPACE_ID}/time_entries"
    response = requests.post(url, json=data, auth=AUTH, headers=HEADERS)
    response.raise_for_status()
    return response.json()

def start_timer(description: str, project_id: int = None) -> dict:
    """
    Start a running timer.
    Toggl represents running timers with duration = -(Unix timestamp of start).
    """
    start = datetime.now(timezone.utc)
    start_unix = int(start.timestamp())
    data = {
        "description": description,
        "start": start.isoformat(),
        "duration": -start_unix,  # Negative Unix timestamp = running timer
        "workspace_id": WORKSPACE_ID,
        "created_with": "replit-integration"
    }
    if project_id:
        data["project_id"] = project_id

    url = f"{BASE_URL}/workspaces/{WORKSPACE_ID}/time_entries"
    response = requests.post(url, json=data, auth=AUTH, headers=HEADERS)
    response.raise_for_status()
    return response.json()

def stop_timer(entry_id: int) -> dict:
    """Stop a running timer by patching its end time."""
    stop_time = datetime.now(timezone.utc)
    url = f"{BASE_URL}/workspaces/{WORKSPACE_ID}/time_entries/{entry_id}/stop"
    response = requests.patch(url, auth=AUTH, headers=HEADERS)
    response.raise_for_status()
    return response.json()

def get_current_timer() -> dict:
    """Get the currently running timer (if any)."""
    response = requests.get(f"{BASE_URL}/me/time_entries/current", auth=AUTH)
    if response.status_code == 200 and response.text != 'null':
        return response.json()
    return None

def get_recent_entries(days: int = 7) -> list:
    """Fetch time entries from the past N days."""
    end = datetime.now(timezone.utc)
    start = end - timedelta(days=days)
    params = {
        "start_date": start.isoformat(),
        "end_date": end.isoformat()
    }
    response = requests.get(f"{BASE_URL}/me/time_entries", auth=AUTH, params=params)
    response.raise_for_status()
    return response.json() or []

# Example usage
if __name__ == "__main__":
    me = get_me()
    print(f"Connected as: {me['fullname']} ({me['email']})")

    projects = get_projects()
    print(f"Projects: {[p['name'] for p in projects[:5]]}")

    # Log a 1-hour completed entry
    end_time = datetime.now(timezone.utc)
    start_time = end_time - timedelta(hours=1)
    entry = create_time_entry(
        description="Integration setup",
        start=start_time,
        end=end_time,
        billable=True
    )
    print(f"Time entry created: {entry['id']} — {entry['description']}")
```

**Expected result:** Running the script prints your Toggl username, lists active projects, and creates a 1-hour time entry visible in your Toggl Track account.

### 4. Generate Reports with Python and Node.js

The Toggl Reports API v3 (at reports.track.toggl.com/api/v3/) provides pre-aggregated time reports that are far more efficient than fetching raw time entries and aggregating manually. It uses the same API token Basic Auth as the main API.

The Reports API offers three report types: Summary (totals by group — project, user, client, tag), Detailed (all individual time entries with filtering), and Weekly (hours per day of week). For client billing, the Detailed report filtered by client_ids is most useful. For team analytics, the Summary report grouped by USER is the quickest way to see per-person hours.

The Node.js server below provides a billing report endpoint that combines data from the main API (to get client and project names) with the Reports API (to get aggregated hours).

```
const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

const API_TOKEN = process.env.TOGGL_API_TOKEN;
const WORKSPACE_ID = process.env.TOGGL_WORKSPACE_ID;

// Both APIs use the same Basic Auth
const AUTH = { username: API_TOKEN, password: 'api_token' };

const togglApi = axios.create({
  baseURL: 'https://api.track.toggl.com/api/v9',
  auth: AUTH
});

const reportsApi = axios.create({
  baseURL: 'https://reports.track.toggl.com/api/v3',
  auth: AUTH
});

// Get all active projects
app.get('/projects', async (req, res) => {
  try {
    const response = await togglApi.get(`/workspaces/${WORKSPACE_ID}/projects`, {
      params: { active: 'true' }
    });
    res.json(response.data || []);
  } catch (err) {
    res.status(err.response?.status || 500).json({ error: err.message });
  }
});

// Get summary report: hours per project for a date range
// Query params: startDate (YYYY-MM-DD), endDate (YYYY-MM-DD)
app.get('/reports/summary', async (req, res) => {
  const { startDate, endDate } = req.query;
  if (!startDate || !endDate) {
    return res.status(400).json({ error: 'startDate and endDate are required (YYYY-MM-DD)' });
  }
  try {
    const response = await reportsApi.post(
      `/workspace/${WORKSPACE_ID}/summary/time_entries`,
      {
        start_date: startDate,
        end_date: endDate,
        grouping: 'projects',
        sub_grouping: 'users'
      }
    );
    // Format: map project groups to readable names
    const summary = (response.data.groups || []).map(group => ({
      project: group.title || 'No Project',
      totalSeconds: group.seconds || 0,
      totalHours: ((group.seconds || 0) / 3600).toFixed(2),
      users: (group.sub_groups || []).map(sg => ({
        user: sg.title,
        hours: (sg.seconds / 3600).toFixed(2)
      }))
    }));
    res.json(summary);
  } catch (err) {
    console.error('Reports error:', err.response?.data);
    res.status(err.response?.status || 500).json({ error: err.message });
  }
});

// Get detailed time entries report
app.get('/reports/detailed', async (req, res) => {
  const { startDate, endDate, projectId } = req.query;
  if (!startDate || !endDate) {
    return res.status(400).json({ error: 'startDate and endDate are required' });
  }
  try {
    const body = {
      start_date: startDate,
      end_date: endDate,
      order_by: 'date',
      order_dir: 'DESC'
    };
    if (projectId) body.project_ids = [parseInt(projectId)];

    const response = await reportsApi.post(
      `/workspace/${WORKSPACE_ID}/search/time_entries`,
      body
    );
    res.json(response.data || []);
  } catch (err) {
    res.status(err.response?.status || 500).json({ error: err.message });
  }
});

// Create a time entry
app.post('/time-entries', async (req, res) => {
  const { description, start, end, projectId, billable } = req.body;
  if (!description || !start || !end) {
    return res.status(400).json({ error: 'description, start, and end are required' });
  }
  const startDate = new Date(start);
  const endDate = new Date(end);
  const durationSeconds = Math.round((endDate - startDate) / 1000);

  try {
    const response = await togglApi.post(`/workspaces/${WORKSPACE_ID}/time_entries`, {
      description,
      start: startDate.toISOString(),
      duration: durationSeconds,
      workspace_id: parseInt(WORKSPACE_ID),
      project_id: projectId ? parseInt(projectId) : undefined,
      billable: billable !== false,
      created_with: 'replit-integration'
    });
    res.status(201).json(response.data);
  } catch (err) {
    console.error('Create entry error:', err.response?.data);
    res.status(err.response?.status || 500).json({ error: err.message });
  }
});

app.listen(3000, '0.0.0.0', () => {
  console.log('Toggl Track integration server running on port 3000');
});
```

**Expected result:** The server starts on port 3000. A GET request to /reports/summary with date parameters returns aggregated hours per project for the specified period.

### 5. Schedule Automated Reports and Deploy

For automated weekly or monthly reports, use Python's schedule library or Node's node-cron package to run report generation on a recurring schedule from a Replit Reserved VM deployment.

The code below is a complete scheduled report script that runs every Monday at 8am, fetches the previous week's time data, and formats a report. Pair this with a notification service (SendGrid for email, Slack webhook for chat) to deliver the report automatically.

For deployment, choose Reserved VM in Replit for scheduled jobs — it keeps your script running continuously with no cold-start delay. Choose Autoscale for web apps that respond to user requests or webhook triggers. Click 'Deploy' in the Replit toolbar, select your deployment type, and configure the run command to start your server or scheduler script.

```
import os
import time
import schedule
import requests
from datetime import datetime, timezone, timedelta

API_TOKEN = os.environ["TOGGL_API_TOKEN"]
WORKSPACE_ID = int(os.environ["TOGGL_WORKSPACE_ID"])
AUTH = (API_TOKEN, "api_token")
REPORTS_URL = f"https://reports.track.toggl.com/api/v3/workspace/{WORKSPACE_ID}"

def seconds_to_hm(seconds: int) -> str:
    """Format seconds as H:MM."""
    h = seconds // 3600
    m = (seconds % 3600) // 60
    return f"{h}h {m:02d}m"

def generate_weekly_report():
    """Generate a weekly summary report and print it."""
    end_date = datetime.now(timezone.utc)
    start_date = end_date - timedelta(days=7)

    payload = {
        "start_date": start_date.strftime("%Y-%m-%d"),
        "end_date": end_date.strftime("%Y-%m-%d"),
        "grouping": "projects",
        "sub_grouping": "users"
    }

    response = requests.post(
        f"{REPORTS_URL}/summary/time_entries",
        json=payload,
        auth=AUTH
    )
    response.raise_for_status()
    data = response.json()

    total_seconds = data.get('seconds', 0)
    print(f"\n=== Weekly Report ({start_date.date()} to {end_date.date()}) ===")
    print(f"Total time: {seconds_to_hm(total_seconds)}\n")

    for group in data.get('groups', []):
        project_name = group.get('title', 'No Project')
        project_seconds = group.get('seconds', 0)
        print(f"  {project_name}: {seconds_to_hm(project_seconds)}")
        for sub in group.get('sub_groups', []):
            print(f"    - {sub.get('title', 'Unknown')}: {seconds_to_hm(sub.get('seconds', 0))}")

    print("\n" + "="*50)
    # In production, send this to Slack or email:
    # send_slack_message(format_report_for_slack(data))
    # send_email_report(format_report_html(data))

# Schedule to run every Monday at 8am UTC
schedule.every().monday.at("08:00").do(generate_weekly_report)

print("Toggl weekly report scheduler started.")
print("Next run:", schedule.next_run())

# Run immediately on startup for testing
generate_weekly_report()

while True:
    schedule.run_pending()
    time.sleep(60)  # Check every minute
```

**Expected result:** Running the scheduler prints the weekly report immediately, then schedules subsequent runs for Monday mornings. With Reserved VM deployment, reports generate automatically each week.

## Best practices

- Always store TOGGL_API_TOKEN and TOGGL_WORKSPACE_ID in Replit Secrets (lock icon 🔒) — never in source code or Git history.
- Cast TOGGL_WORKSPACE_ID to an integer using int() in Python — os.environ returns strings, but Toggl's API requires workspace_id as an integer in the request body.
- Remember that Toggl's Basic Auth uses the API token as the username and the literal string 'api_token' as the password — this is Toggl-specific and different from all other API auth patterns.
- Use the Reports API (reports.track.toggl.com) for aggregated data like billable totals and project summaries — it is significantly more efficient than fetching and aggregating raw time entries.
- Note that the Reports API v3 uses POST requests for all report queries — not GET — with filter parameters in the request body.
- Always include timezone information in datetime objects — Toggl rejects naive datetimes without timezone offsets.
- Deploy with Reserved VM for scheduled report generators that must run at specific times (weekly summaries, monthly invoices); use Autoscale for web apps that respond to user requests.
- Cache Reports API responses for at least 15 minutes — Toggl data has some processing delay and repeated identical queries waste API capacity.

## Use cases

### Automated Time Logging from Project Management Tools

When a developer closes a GitHub issue or a team member marks a task complete in Asana or Jira, a Replit webhook automatically creates a Toggl time entry for the time spent on that task. The integration reads start and end timestamps from the task history and posts a corresponding Toggl entry against the linked client project, eliminating manual time logging entirely.

Prompt example:

```
Build a Flask webhook endpoint that receives a task completion event with start_time, end_time, task_name, and project_id, looks up the corresponding Toggl project ID from a mapping table, and creates a time entry using TOGGL_API_TOKEN and TOGGL_WORKSPACE_ID from Replit Secrets.
```

### Client Invoice Report Generator

Build a Replit web app that pulls all billable time entries for a specific client from the Toggl Reports API, calculates the invoice amount based on hourly rates stored in your database, and generates a formatted invoice PDF or spreadsheet. Account managers can trigger a report for any client and billing period from a simple web interface without needing access to Toggl.

Prompt example:

```
Create an Express server with a POST /invoice endpoint that accepts client_id, start_date, end_date, and hourly_rate, fetches all billable time entries for that client from the Toggl Reports API, and returns a JSON invoice summary showing hours by project and total amount due.
```

### Weekly Productivity Summary Digest

Create a scheduled Replit job that runs every Monday morning, fetches the previous week's time data from Toggl for all team members, and sends a formatted summary to a Slack channel or email. The summary shows total hours per project, billable vs non-billable split, and highlights any team members who logged significantly fewer hours than average.

Prompt example:

```
Write a Python script that fetches the Toggl detailed report for the past 7 days using the Reports API, groups entries by user and project, calculates total and billable hours per person, and formats a weekly summary with utilization percentage for each team member.
```

## Troubleshooting

### All API requests return 403 Forbidden

Cause: The Toggl API token is incorrect, or the Basic Auth is configured wrong. Toggl requires the API token as the username and the literal string 'api_token' as the password — not the token as a bearer token or API key header.

Solution: Verify your API token from Toggl Profile Settings > API Token. Check that your Basic Auth uses the token as the username and exactly 'api_token' (lowercase, no spaces) as the password. Do not use Authorization: Bearer — Toggl requires HTTP Basic Auth for this unusual authentication pattern.

```
# Python: correct Toggl authentication
AUTH = (os.environ['TOGGL_API_TOKEN'], 'api_token')  # Token as user, 'api_token' as password

# Node.js equivalent with axios:
# auth: { username: process.env.TOGGL_API_TOKEN, password: 'api_token' }
```

### POST time entry returns 400 — 'Required field missing' or validation error

Cause: The request body is missing required fields or uses incorrect data types. Toggl's time entry endpoint requires workspace_id as an integer (not string), and duration as a positive integer for completed entries.

Solution: Ensure workspace_id is an integer (not a string from os.environ which returns strings). Cast with int(os.environ['TOGGL_WORKSPACE_ID']). Duration must be a positive integer in seconds for completed entries. The start timestamp must be ISO 8601 with timezone offset.

```
# Python: ensure correct data types
WORKSPACE_ID = int(os.environ['TOGGL_WORKSPACE_ID'])  # Must be integer

data = {
    'workspace_id': WORKSPACE_ID,  # Integer, not string
    'duration': int(duration_seconds),  # Integer seconds
    'start': start.isoformat(),  # String with timezone
    'description': description  # String (can be empty but must be present)
}
```

### Reports API returns 404 — endpoint not found

Cause: The Reports API uses a different base URL (reports.track.toggl.com) than the main Toggl API (api.track.toggl.com). Using the main API base URL for report endpoints causes 404 errors.

Solution: Use the correct Reports API base URL: https://reports.track.toggl.com/api/v3/. The workspace ID in the path should be numeric. Report endpoints also use POST (not GET) for filter operations, which is different from typical REST conventions.

```
# Python: use correct Reports API URL and method
REPORTS_URL = f"https://reports.track.toggl.com/api/v3/workspace/{WORKSPACE_ID}"
# Reports use POST even for read operations:
response = requests.post(
    f"{REPORTS_URL}/summary/time_entries",
    json={"start_date": start_date, "end_date": end_date},
    auth=AUTH
)
```

### get_current_timer returns null even when a timer is running in the Toggl app

Cause: The GET /me/time_entries/current endpoint returns the literal string 'null' (not a JSON null) when no timer is running. If you call response.json() on this response, it returns Python's None. Also, the timer must be running on the same account as the API token.

Solution: Check if response.text == 'null' before calling response.json(). The timer must be associated with the account whose API token you are using. Timers started by other team members in a shared workspace will not appear in your /me/time_entries/current endpoint.

```
def get_current_timer() -> dict | None:
    response = requests.get(f"{BASE_URL}/me/time_entries/current", auth=AUTH)
    if response.status_code == 200 and response.text.strip() != 'null':
        return response.json()
    return None  # No timer running
```

## Frequently asked questions

### How do I store my Toggl API token in Replit?

Click the lock icon 🔒 in the left sidebar of your Replit project to open the Secrets pane. Add TOGGL_API_TOKEN with your 32-character API token from Toggl Profile Settings > API Token, and TOGGL_WORKSPACE_ID with your numeric workspace ID. Access them in Python with os.environ['TOGGL_API_TOKEN'] and in Node.js with process.env.TOGGL_API_TOKEN.

### Why does Toggl use 'api_token' as the password in Basic Auth?

This is a historical design choice by Toggl that predates modern API key authentication standards. The Basic Auth format (username:password) was repurposed: the API token serves as the username, and the literal string 'api_token' is a fixed password placeholder. This pattern works for both personal API tokens and OAuth access tokens — the password field is always 'api_token' regardless of which credential type you use.

### Can I use the Toggl API on the free tier?

Yes. Toggl Track's free plan includes full API access for the main Toggl API and basic Reports API functionality. Advanced reporting features (like team analytics across multiple users) require a paid Starter or Premium plan. The free tier is fully functional for single-user integrations and personal time tracking automation.

### What is the difference between the Toggl API and Toggl Reports API?

The main Toggl API (api.track.toggl.com/api/v9/) handles CRUD operations: creating time entries, managing projects and clients, and reading raw time entry data. The Reports API (reports.track.toggl.com/api/v3/) provides pre-aggregated analytics: summary totals by project/user, detailed filterable entry lists, and weekly distributions. For billing and analytics use the Reports API; for creating and modifying time data use the main API.

### How do I represent a running timer in the Toggl API?

Toggl represents running (in-progress) timers with a negative duration: duration = -(Unix timestamp of start time in seconds). This lets Toggl calculate elapsed time as: time.now() + duration (since duration is negative, this subtraction gives elapsed seconds). When creating a running timer via the API, set duration to -int(time.time()) at the moment you start it. To stop it, use the /stop endpoint which sets the end time and calculates the final positive duration.

### Does Replit work with Toggl Track webhooks?

Toggl Track supports webhooks on paid plans (Starter and above). After deploying your Replit app to get a stable URL, register your webhook endpoint in Toggl under Workspace Settings > Webhooks. Toggl can notify your Replit app when time entries are created, stopped, or deleted. Webhooks require a deployed URL — development preview URLs will not work for production webhooks.

---

Source: https://www.rapidevelopers.com/replit-integration/toggl
© RapidDev — https://www.rapidevelopers.com/replit-integration/toggl
