API Documentation

The Contractor's Work Log REST API lets you programmatically manage work logs, time entries, companies, invoices, and timers. Use it to integrate time tracking into your own tools, scripts, or apps.

API access is available on plans that include the API feature. You can check your plan on the pricing page.

Authentication

All API requests must be authenticated. Two methods are supported:

HTTP Basic Auth

Use your CWorkLog username and password. Pass them as the Basic Auth username and password with each request.

API Key (Bearer Token)

Generate an API key from your account settings. Send it in the Authorization header as a Bearer token.

Basic Auth Example
curl -u "your_username:your_password" \
  http://cworklog.com/api/status
Bearer Token Example
curl -H "Authorization: Bearer YOUR_API_KEY" \
  http://cworklog.com/api/status
Keep your API key secret. Do not expose it in client-side code, public repositories, or shared logs.

Base URL

All endpoints are relative to the base URL below.

Base URL http://cworklog.com/api

Response Format

All responses are returned as JSON. Successful responses include the data directly. Error responses include an error field with a human-readable message.

Success Response
{
  "success": true,
  "data": { ... }
}
Error Response
{
  "error": "Work log not found"
}

Rate Limiting

API requests are rate limited to ensure fair usage and service stability. If you exceed the limit, you will receive a 429 Too Many Requests response.

Rate limit details and headers will be documented here as they are finalized. For now, we recommend keeping requests under 60 per minute.

Errors

The API uses standard HTTP status codes to indicate success or failure.

Status Code Meaning
200 Success. The request completed as expected.
400 Bad Request. Missing or invalid parameters.
401 Unauthorized. Invalid or missing authentication credentials.
403 Forbidden. Your plan does not include API access.
404 Not Found. The requested resource does not exist.
429 Too Many Requests. Rate limit exceeded.
500 Internal Server Error. Something went wrong on our end.

Work Logs

GET /worklogs

Retrieve all work logs for the authenticated user. Also available as POST /worklogs/list with body parameters.

Query Parameters
NameTypeDescription
include_lockedoptional boolean Include locked work logs in the results. Default: false.
locked_onlyoptional boolean Return only locked work logs. Default: false.
Example Request
curl -u "jsmith:secret123" \
  "http://cworklog.com/api/worklogs?include_locked=1"
Example Response
{
  "success": true,
  "data": [
    {
      "id": 102,
      "title": "Website Redesign",
      "company_id": 5,
      "company_name": "Acme Corp",
      "description": "Full redesign of marketing site",
      "locked": 0,
      "created": "2025-09-15 08:30:00",
      "total_time_seconds": 28800,
      "total_time_formatted": "8:00:00"
    },
    {
      "id": 103,
      "title": "Mobile App Backend",
      "company_id": 5,
      "company_name": "Acme Corp",
      "description": "REST API for mobile application",
      "locked": 0,
      "created": "2025-10-01 14:00:00",
      "total_time_seconds": 14400,
      "total_time_formatted": "4:00:00"
    }
  ]
}

POST /worklogs/new

Create a new work log.

Body Parameters
NameTypeDescription
company_idrequired integer The company/client ID. Aliases: cid, client_id.
titlerequired string Title of the work log.
descriptionoptional string Longer description of the work log.
notesoptional string Private notes for the work log.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/worklogs/new \
  -d "company_id=5&title=Logo+Design&description=New+branding+project"
Example Response
{
  "success": true,
  "data": {
    "id": 147,
    "title": "Logo Design",
    "company_id": 5,
    "description": "New branding project",
    "created": "2025-11-20 10:15:32"
  }
}

POST /worklogs/update

Update a single field on an existing work log.

Body Parameters
NameTypeDescription
idrequired integer Work log ID to update.
column_keyrequired string The field to update (e.g. title, description, notes).
valuerequired string The new value for the field.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/worklogs/update \
  -d "id=147&column_key=title&value=Updated+Logo+Design"
Example Response
{
  "success": true
}

POST /worklogs/delete

Delete a work log and all associated time entries.

Body Parameters
NameTypeDescription
idrequired integer Work log ID to delete.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/worklogs/delete \
  -d "id=147"
Example Response
{
  "success": true
}

POST /worklogs/timelogs

Get all time log entries for a specific work log.

Body Parameters
NameTypeDescription
widrequired integer Work log ID. Alias: id.
optsoptional object Additional options for filtering or formatting.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/worklogs/timelogs \
  -d "wid=102"
Example Response
{
  "success": true,
  "data": [
    {
      "id": 501,
      "work_log_id": 102,
      "start_time": "2025-10-12 09:00:00",
      "stop_time": "2025-10-12 12:30:00",
      "notes": "Header and navigation layout",
      "duration_seconds": 12600,
      "duration_formatted": "3:30:00"
    },
    {
      "id": 502,
      "work_log_id": 102,
      "start_time": "2025-10-12 13:30:00",
      "stop_time": "2025-10-12 17:00:00",
      "notes": "Footer and responsive styles",
      "duration_seconds": 12600,
      "duration_formatted": "3:30:00"
    }
  ]
}

Time Logs

POST /timelogs/new

Create a new time log entry for a work log.

Body Parameters
NameTypeDescription
worklog_idrequired integer The work log to attach this time entry to.
start_timerequired string Start time in YYYY-MM-DD HH:MM:SS format.
stop_timerequired string Stop time in YYYY-MM-DD HH:MM:SS format.
notesoptional string Notes describing the work done.
optsoptional object Additional options.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/new \
  -d "worklog_id=102&start_time=2025-10-14+09:00:00&stop_time=2025-10-14+12:00:00&notes=API+integration+work"
Example Response
{
  "success": true,
  "data": {
    "id": 510,
    "work_log_id": 102,
    "start_time": "2025-10-14 09:00:00",
    "stop_time": "2025-10-14 12:00:00",
    "notes": "API integration work",
    "duration_seconds": 10800
  }
}

POST /timelogs

List time logs for a specific work log. Functionally equivalent to POST /worklogs/timelogs.

Body Parameters
NameTypeDescription
widrequired integer Work log ID. Alias: id.
optsoptional object Additional options.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs \
  -d "wid=102"
Example Response
{
  "success": true,
  "data": [
    {
      "id": 501,
      "work_log_id": 102,
      "start_time": "2025-10-12 09:00:00",
      "stop_time": "2025-10-12 12:30:00",
      "notes": "Header layout",
      "duration_seconds": 12600
    }
  ]
}

POST /timelogs/copy

Copy one or more time log entries to a different work log.

Body Parameters
NameTypeDescription
timelog_idsrequired string|array Comma-separated IDs or array of time log IDs to copy. Aliases: timelogs, rows.
to_worklog_idrequired integer Destination work log ID.
note_overrideoptional string Override notes on the copied entries.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/copy \
  -d "timelog_ids=501,502&to_worklog_id=103"
Example Response
{
  "success": true,
  "data": {
    "copied_count": 2
  }
}

POST /timelogs/move

Move time log entries to a different work log.

Body Parameters
NameTypeDescription
timelogsrequired array Array of time log row objects to move.
to_worklog_idrequired integer Destination work log ID.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/move \
  -H "Content-Type: application/json" \
  -d '{"timelogs": [{"id": 501}, {"id": 502}], "to_worklog_id": 103}'
Example Response
{
  "success": true,
  "data": {
    "moved_count": 2
  }
}

POST /timelogs/upload

Bulk upload multiple time log entries at once.

Body Parameters
NameTypeDescription
timelogsrequired array Array of objects with work_log_id, start_time, stop_time, notes, and optional opts. Alias: rows.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/upload \
  -H "Content-Type: application/json" \
  -d '{
  "timelogs": [
    {
      "work_log_id": 102,
      "start_time": "2025-10-15 09:00:00",
      "stop_time": "2025-10-15 11:00:00",
      "notes": "Morning session"
    },
    {
      "work_log_id": 102,
      "start_time": "2025-10-15 13:00:00",
      "stop_time": "2025-10-15 16:30:00",
      "notes": "Afternoon session"
    }
  ]
}'
Example Response
{
  "success": true,
  "data": {
    "uploaded_count": 2
  }
}

POST /timelogs/update_start_time

Update the start time of a time log entry.

Body Parameters
NameTypeDescription
timelog_idrequired integer Time log ID. Alias: id.
start_timerequired string New start time in YYYY-MM-DD HH:MM:SS format.
notesoptional string Updated notes.
optsoptional object Additional options.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/update_start_time \
  -d "timelog_id=501&start_time=2025-10-12+08:45:00"
Example Response
{
  "success": true
}

POST /timelogs/update_stop_time

Update the stop time of a time log entry. You can pass a datetime string or an integer number of minutes.

Body Parameters
NameTypeDescription
timelog_idrequired integer Time log ID. Alias: id.
stop_time_or_minutes_intrequired string|integer New stop time (YYYY-MM-DD HH:MM:SS) or total duration in minutes from start.
notesoptional string Updated notes.
optsoptional object Additional options.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/update_stop_time \
  -d "timelog_id=501&stop_time_or_minutes_int=2025-10-12+13:00:00"
Example Response
{
  "success": true
}

POST /timelogs/update_stop_time_subtract_min

Subtract a number of minutes from the stop time of a time log entry.

Body Parameters
NameTypeDescription
widrequired integer Work log ID.
timelog_idrequired integer Time log ID. Alias: id.
stop_time_subtract_minutesrequired integer Number of minutes to subtract from the current stop time.
optsoptional object Additional options.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/update_stop_time_subtract_min \
  -d "wid=102&timelog_id=501&stop_time_subtract_minutes=15"
Example Response
{
  "success": true
}

POST /timelogs/update_notes

Update only the notes on a time log entry.

Body Parameters
NameTypeDescription
timelog_idrequired integer Time log ID. Alias: id.
notesrequired string New notes text.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/update_notes \
  -d "timelog_id=501&notes=Updated+header+layout+with+sticky+nav"
Example Response
{
  "success": true
}

POST /timelogs/delete

Delete a time log entry.

Body Parameters
NameTypeDescription
timelog_idrequired integer Time log ID. Aliases: time_log_id, tid, id.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/timelogs/delete \
  -d "timelog_id=501"
Example Response
{
  "success": true
}

Timer

POST /start

Start a timer on a work log. Creates a time log entry with a start time and no stop time.

Body Parameters
NameTypeDescription
widrequired integer Work log ID to start the timer on.
start_timeoptional string Backdate the start time. Format: YYYY-MM-DD HH:MM:SS. Defaults to now.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/start \
  -d "wid=102"
Example Response
{
  "success": true,
  "data": {
    "timelog_id": 515,
    "work_log_id": 102,
    "start_time": "2025-11-20 14:30:05"
  }
}

POST /stop

Stop a running timer on a work log. Sets the stop time on the open time log entry.

Body Parameters
NameTypeDescription
widrequired integer Work log ID to stop the timer on.
timelog_idoptional integer Specific time log entry to stop (if multiple timers are running).
notesoptional string Notes to attach to this time entry.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/stop \
  -d "wid=102&notes=Finished+header+component"
Example Response
{
  "success": true,
  "data": {
    "timelog_id": 515,
    "work_log_id": 102,
    "start_time": "2025-11-20 14:30:05",
    "stop_time": "2025-11-20 16:45:12",
    "duration_seconds": 8107,
    "duration_formatted": "2:15:07"
  }
}

Companies

GET /companies

Retrieve all companies (clients) for the authenticated user.

This endpoint takes no parameters.
Example Request
curl -u "jsmith:secret123" \
  http://cworklog.com/api/companies
Example Response
{
  "success": true,
  "data": [
    {
      "id": 5,
      "name": "Acme Corp",
      "street": "123 Main St",
      "city": "Springfield",
      "state": "IL",
      "zip": "62704",
      "country": "US",
      "phone": "555-0142",
      "email": "billing@acmecorp.example",
      "default_hourly_rate": "75.00"
    },
    {
      "id": 8,
      "name": "Globex Industries",
      "street": "456 Oak Ave",
      "city": "Shelbyville",
      "state": "IL",
      "zip": "62705",
      "country": "US",
      "phone": "",
      "email": "accounts@globex.example",
      "default_hourly_rate": "100.00"
    }
  ]
}

POST /companies/new

Create a new company (client).

Body Parameters
NameTypeDescription
namerequired string Company name.
streetoptional string Street address line 1.
street2optional string Street address line 2.
cityoptional string City.
stateoptional string State or province.
zipoptional string ZIP or postal code.
countryoptional string Country code or name.
phoneoptional string Phone number.
emailoptional string Contact email address.
notesoptional string Private notes about the company.
default_hourly_rateoptional number Default hourly rate for this client.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/companies/new \
  -d "name=Initech&city=Austin&state=TX&email=contact@initech.example&default_hourly_rate=85"
Example Response
{
  "success": true,
  "data": {
    "id": 12,
    "name": "Initech"
  }
}

POST /companies/delete

Delete a company and its associated work logs.

Body Parameters
NameTypeDescription
idrequired integer Company ID to delete.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/companies/delete \
  -d "id=12"
Example Response
{
  "success": true
}
This action is destructive and cannot be undone. All work logs and time entries under this company will be permanently deleted.

User

GET /status

Get the current user status including running timers, plan info, and account details. Useful as a health check or to retrieve active timers.

This endpoint takes no parameters.
Example Request
curl -u "jsmith:secret123" \
  http://cworklog.com/api/status
Example Response
{
  "success": true,
  "data": {
    "user": {
      "id": 42,
      "username": "jsmith",
      "email": "jsmith@example.com",
      "timezone": "America/New_York"
    },
    "plan": {
      "name": "Professional",
      "max_clients": -1,
      "max_active_worklogs": -1,
      "allow_api_key": 1
    },
    "unfinished_timers": [
      {
        "timelog_id": 515,
        "work_log_id": 102,
        "work_log_title": "Website Redesign",
        "start_time": "2025-11-20 14:30:05"
      }
    ]
  }
}

POST /user/set_timezone

Set the timezone for the authenticated user. Affects how times are displayed and recorded.

Body Parameters
NameTypeDescription
timezonerequired string IANA timezone identifier (e.g. America/New_York, Europe/London).
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/user/set_timezone \
  -d "timezone=America/Chicago"
Example Response
{
  "success": true
}

POST /user/set_auto_upgrade

Enable or disable automatic plan upgrades when usage limits are reached.

Body Parameters
NameTypeDescription
enabledrequired boolean Set to 1 or true to enable, 0 or false to disable.
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/user/set_auto_upgrade \
  -d "enabled=1"
Example Response
{
  "success": true
}

GET /plans

List all available subscription plans.

This endpoint takes no parameters.
Example Request
curl -u "jsmith:secret123" \
  http://cworklog.com/api/plans
Example Response
{
  "success": true,
  "data": [
    {
      "name": "Free",
      "shortname": "free",
      "cost_monthly": "0.00",
      "max_clients": 2,
      "max_active_worklogs": 3,
      "allow_api_key": 0
    },
    {
      "name": "Professional",
      "shortname": "pro",
      "cost_monthly": "9.00",
      "max_clients": -1,
      "max_active_worklogs": -1,
      "allow_api_key": 1
    }
  ]
}

Invoices

GET /invoices

Retrieve invoices. Optionally filter by work log.

Query Parameters
NameTypeDescription
work_log_idoptional integer Filter invoices to a specific work log.
Example Request
curl -u "jsmith:secret123" \
  "http://cworklog.com/api/invoices?work_log_id=102"
Example Response
{
  "success": true,
  "data": [
    {
      "id": 31,
      "work_log_id": 102,
      "invoice_number": "INV-2025-031",
      "amount_billed_total": "600.00",
      "amount_clocked": "525.00",
      "tax_rate": "0.00",
      "amount_tax": "0.00",
      "created": "2025-10-31 10:00:00"
    }
  ]
}

GET /invoices/email_templates

Retrieve the list of available email templates for invoice delivery.

This endpoint takes no parameters.
Example Request
curl -u "jsmith:secret123" \
  http://cworklog.com/api/invoices/email_templates
Example Response
{
  "success": true,
  "data": [
    {
      "key": "default",
      "name": "Default Invoice Template"
    },
    {
      "key": "minimal",
      "name": "Minimal Template"
    }
  ]
}

GET /invoices/email_preview

Generate a preview of the invoice email before sending.

Query Parameters
NameTypeDescription
work_log_idrequired integer Work log ID for the invoice.
templateoptional string Email template key.
invoice_numberoptional string Invoice number to display.
messageoptional string Custom message to include in the email body.
amount_billed_totaloptional number Total amount billed.
from_nameoptional string Sender name to display in the email.
Example Request
curl -u "jsmith:secret123" \
  "http://cworklog.com/api/invoices/email_preview?work_log_id=102&template=default&invoice_number=INV-2025-032&amount_billed_total=750.00&from_name=John+Smith"
Example Response
{
  "success": true,
  "data": {
    "html": "<html>...rendered email preview HTML...</html>",
    "subject": "Invoice INV-2025-032 from John Smith"
  }
}

POST /invoices/email

Send an invoice email to a client with an optional PDF attachment.

Body Parameters
NameTypeDescription
work_log_idrequired integer Work log ID for the invoice.
to_emailrequired string Recipient email address.
subjectrequired string Email subject line.
htmlrequired string HTML content of the email body.
invoice_numberrequired string Invoice number.
amount_billed_totalrequired number Total amount billed.
pdfoptional string Base64-encoded PDF to attach.
cc_emailoptional string CC email address.
messageoptional string Custom message to include.
email_templateoptional string Email template key.
amount_clockedoptional number Amount based on clocked hours.
amount_creditedoptional number Credits applied.
amount_taxoptional number Tax amount.
tax_rateoptional number Tax rate percentage.
from_nameoptional string Sender display name.
private_notesoptional string Private notes (not shown to client, saved for your records).
Example Request
curl -u "jsmith:secret123" \
  -X POST http://cworklog.com/api/invoices/email \
  -H "Content-Type: application/json" \
  -d '{
  "work_log_id": 102,
  "to_email": "billing@acmecorp.example",
  "subject": "Invoice INV-2025-032 from John Smith",
  "html": "<h1>Invoice</h1><p>Amount due: $750.00</p>",
  "invoice_number": "INV-2025-032",
  "amount_billed_total": 750.00,
  "amount_clocked": 675.00,
  "tax_rate": 0,
  "amount_tax": 0,
  "from_name": "John Smith"
}'
Example Response
{
  "success": true,
  "data": {
    "invoice_id": 32,
    "sent_to": "billing@acmecorp.example"
  }
}