> This page is for API, version 2026-01-01 (Stable) (default).
> For other versions, use one of these documentation indexes:
> - 2026-01-01 (Stable) (default): https://developer.deel.com/api/stable/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.deel.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.deel.com/_mcp/server.

# Working with timesheets

> Learn how to create, manage, and track contractor timesheets using the API

## Overview

The Timesheets API allows you to programmatically submit, review, and track hourly work for time-based contractor contracts. Both contractors and clients can create timesheets, which are then reviewed and included in the next invoice for the payment cycle.

This guide covers the complete timesheet workflow from creation through approval and payment tracking.

## When to use this workflow

Use the Timesheets API when you need to:

* Submit hourly work logs for time-based contracts
* Review and approve contractor-submitted timesheets
* Track billable hours for project-based work
* Automate timesheet creation from time-tracking systems
* Manage hourly report presets for recurring work types

## Prerequisites

Before you begin, ensure you have:

* A valid API token with `timesheets:write` and `timesheets:read` scopes
* The `contract_id` of the time-based contractor contract
* Work details including date, hours worked, and description
* (Optional) A timesheet preset ID if using predefined hourly rates

> **Note**
>
> Timesheets can only be created for time-based contracts. Fixed-price and milestone contracts do not support timesheet submission.

## Step-by-step workflow

This example demonstrates submitting 40 hours of backend development work for a contractor's weekly timesheet.

### Identify the contract

First, retrieve the contract ID for the time-based contractor. You can list contracts and filter by type.

**`Python`**

```python Python
import requests
import os

response = requests.get(
    'https://api.letsdeel.com/rest/contracts',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}'
    },
    params={
        'type': 'ongoing_time_based'
    }
)

contracts = response.json()['data']
contract_id = contracts[0]['id']
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const response = await axios.get(
  'https://api.letsdeel.com/rest/contracts',
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
    },
    params: {
      type: 'ongoing_time_based'
    }
  }
);

const contracts = response.data.data;
const contractId = contracts[0].id;
```

Response:

```json focus={4,6-7}
{
  "data": [
    {
      "id": "c3f9a1d2",
      "title": "Backend Developer - Hourly",
      "type": "ongoing_time_based",
      "status": "active",
      "worker_first_name": "Elena",
      "worker_last_name": "Rodriguez",
      "compensation_details": {
        "amount": 85.00,
        "currency_code": "USD",
        "frequency": "hourly"
      }
    }
  ]
}
```

Save the `id` field - this is your `contract_id` for timesheet creation.

### Create a timesheet entry

Submit the hours worked with a clear description of the work performed.

**`Python`**

```python Python
import requests
import os
from datetime import date

timesheet_data = {
    "data": {
        "contract_id": "c3f9a1d2",
        "quantity": 40,
        "date": str(date.today()),
        "description": "Backend API development - implemented user authentication endpoints and payment processing integration"
    }
}

response = requests.post(
    'https://api.letsdeel.com/rest/timesheets',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
        'Content-Type': 'application/json'
    },
    json=timesheet_data
)

timesheet = response.json()
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const timesheetData = {
  data: {
    contract_id: 'c3f9a1d2',
    quantity: 40,
    date: new Date().toISOString().split('T')[0],
    description: 'Backend API development - implemented user authentication endpoints and payment processing integration'
  }
};

const response = await axios.post(
  'https://api.letsdeel.com/rest/timesheets',
  timesheetData,
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
      'Content-Type': 'application/json'
    }
  }
);

const timesheet = response.data;
```

> **Tip**
>
> Include specific details in the description to help reviewers understand the work performed. This aids in approval and provides context for invoicing.

Response:

```json focus={3-5,7-8}
{
  "data": {
    "id": "ts_9876543210",
    "contract_id": "c3f9a1d2",
    "quantity": 40,
    "date": "2026-02-05",
    "description": "Backend API development - implemented user authentication endpoints and payment processing integration",
    "status": "pending",
    "created_at": "2026-02-05T14:30:00Z"
  }
}
```

The timesheet is now created with `pending` status, waiting for review.

### Using hourly report presets

For recurring work types, create reusable presets with predefined rates and descriptions.

**`Python`**

```python Python
import requests
import os

preset_data = {
    "data": {
        "contract_id": "c3f9a1d2",
        "title": "Backend Development",
        "description": "Standard backend development work including API implementation, database optimization, and code reviews",
        "rate": 85.00
    }
}

response = requests.post(
    'https://api.letsdeel.com/rest/timesheets/presets',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
        'Content-Type': 'application/json'
    },
    json=preset_data
)

preset = response.json()
preset_id = preset['data']['id']
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const presetData = {
  data: {
    contract_id: 'c3f9a1d2',
    title: 'Backend Development',
    description: 'Standard backend development work including API implementation, database optimization, and code reviews',
    rate: 85.00
  }
};

const response = await axios.post(
  'https://api.letsdeel.com/rest/timesheets/presets',
  presetData,
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
      'Content-Type': 'application/json'
    }
  }
);

const preset = response.data;
const presetId = preset.data.id;
```

Once created, use the preset ID when submitting timesheets:

**`Python`**

```python Python
timesheet_data = {
    "data": {
        "contract_id": "c3f9a1d2",
        "quantity": 40,
        "date": "2026-02-05",
        "hourly_report_preset_id": preset_id
    }
}

response = requests.post(
    'https://api.letsdeel.com/rest/timesheets',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
        'Content-Type': 'application/json'
    },
    json=timesheet_data
)
```

**`Node.js`**

```javascript Node.js
const timesheetData = {
  data: {
    contract_id: 'c3f9a1d2',
    quantity: 40,
    date: '2026-02-05',
    hourly_report_preset_id: presetId
  }
};

const response = await axios.post(
  'https://api.letsdeel.com/rest/timesheets',
  timesheetData,
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
      'Content-Type': 'application/json'
    }
  }
);
```

The preset automatically applies the rate and description, streamlining timesheet submission.

### Review and approve timesheets

Clients can review and approve submitted timesheets. This step moves them from `pending` to `approved` status.

**`Python`**

```python Python
import requests
import os

review_data = {
    "data": {
        "status": "approved",
        "reason": "Work verified and approved. Quality meets expectations."
    }
}

response = requests.post(
    f'https://api.letsdeel.com/rest/timesheets/ts_9876543210/reviews',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
        'Content-Type': 'application/json'
    },
    json=review_data
)
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const reviewData = {
  data: {
    status: 'approved',
    reason: 'Work verified and approved. Quality meets expectations.'
  }
};

const response = await axios.post(
  'https://api.letsdeel.com/rest/timesheets/ts_9876543210/reviews',
  reviewData,
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
      'Content-Type': 'application/json'
    }
  }
);
```

> **Note**
>
> The `reason` field is optional but recommended. It creates an audit trail and helps explain approval or rejection decisions.

To reject a timesheet, set `status` to `"rejected"`:

```json
{
  "data": {
    "status": "rejected",
    "reason": "Hours do not match project tracking system. Please verify and resubmit."
  }
}
```

### Retrieve timesheet details

Check the status and details of a specific timesheet.

**`Python`**

```python Python
import requests
import os

response = requests.get(
    'https://api.letsdeel.com/rest/timesheets/ts_9876543210',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}'
    }
)

timesheet = response.json()
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const response = await axios.get(
  'https://api.letsdeel.com/rest/timesheets/ts_9876543210',
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
    }
  }
);

const timesheet = response.data;
```

Response:

```json focus={4,8,20-24}
{
  "data": {
    "id": "ts_9876543210",
    "status": "approved",
    "contract_id": "c3f9a1d2",
    "quantity": 40,
    "date": "2026-02-05",
    "description": "Backend API development - implemented user authentication endpoints and payment processing integration",
    "created_at": "2026-02-05T14:30:00Z",
    "contract": {
      "id": "c3f9a1d2",
      "title": "Backend Developer - Hourly"
    },
    "reported_by": {
      "id": "user_123",
      "full_name": "Elena Rodriguez"
    },
    "reviewed_by": {
      "id": "reviewer_456",
      "full_name": "Michael Chen",
      "reviewed_at": "2026-02-05T16:45:00Z",
      "remarks": "Work verified and approved. Quality meets expectations."
    },
    "hourly_report_preset": {
      "id": "preset_789",
      "rate": 85.00,
      "title": "Backend Development",
      "description": "Standard backend development work"
    }
  }
}
```

### Track payment status

Monitor when approved timesheets are paid by checking the status periodically.

**`Python`**

```python Python
import requests
import os

response = requests.get(
    'https://api.letsdeel.com/rest/timesheets/ts_9876543210',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}'
    }
)

status = response.json()['data']['status']

if status == 'paid':
    print('Timesheet has been paid')
elif status == 'approved':
    print('Timesheet approved, pending payment')
elif status == 'pending':
    print('Timesheet awaiting review')
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const response = await axios.get(
  'https://api.letsdeel.com/rest/timesheets/ts_9876543210',
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
    }
  }
);

const status = response.data.data.status;

if (status === 'paid') {
  console.log('Timesheet has been paid');
} else if (status === 'approved') {
  console.log('Timesheet approved, pending payment');
} else if (status === 'pending') {
  console.log('Timesheet awaiting review');
}
```

Once the invoice is paid, the timesheet status changes to `paid`.

## Common scenarios

### Scenario 1: Submitting multiple days of work

For contractors tracking work across multiple days, submit separate timesheet entries for each day.

**`Python`**

```python Python
import requests
import os

days_worked = [
    {"date": "2026-02-03", "hours": 8, "description": "Database schema design and migration scripts"},
    {"date": "2026-02-04", "hours": 9, "description": "API endpoint implementation for user management"},
    {"date": "2026-02-05", "hours": 8, "description": "Unit tests and integration tests for authentication"},
    {"date": "2026-02-06", "hours": 7.5, "description": "Code review and bug fixes from QA testing"},
    {"date": "2026-02-07", "hours": 7.5, "description": "Performance optimization and documentation"}
]

for day in days_worked:
    timesheet_data = {
        "data": {
            "contract_id": "c3f9a1d2",
            "quantity": day["hours"],
            "date": day["date"],
            "description": day["description"]
        }
    }

    response = requests.post(
        'https://api.letsdeel.com/rest/timesheets',
        headers={
            'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
            'Content-Type': 'application/json'
        },
        json=timesheet_data
    )

    print(f"Created timesheet for {day['date']}: {response.json()['data']['id']}")
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const daysWorked = [
  { date: '2026-02-03', hours: 8, description: 'Database schema design and migration scripts' },
  { date: '2026-02-04', hours: 9, description: 'API endpoint implementation for user management' },
  { date: '2026-02-05', hours: 8, description: 'Unit tests and integration tests for authentication' },
  { date: '2026-02-06', hours: 7.5, description: 'Code review and bug fixes from QA testing' },
  { date: '2026-02-07', hours: 7.5, description: 'Performance optimization and documentation' }
];

for (const day of daysWorked) {
  const timesheetData = {
    data: {
      contract_id: 'c3f9a1d2',
      quantity: day.hours,
      date: day.date,
      description: day.description
    }
  };

  const response = await axios.post(
    'https://api.letsdeel.com/rest/timesheets',
    timesheetData,
    {
      headers: {
        'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
        'Content-Type': 'application/json'
      }
    }
  );

  console.log(`Created timesheet for ${day.date}: ${response.data.data.id}`);
}
```

This creates individual timesheet entries for each day, making it easier to track daily work breakdown.

### Scenario 2: Auto-approved timesheets

For pre-authorized work or trusted contractors, use auto-approval to bypass manual review.

**`Python`**

```python Python
import requests
import os

timesheet_data = {
    "data": {
        "contract_id": "c3f9a1d2",
        "quantity": 40,
        "date": "2026-02-05",
        "description": "Weekly maintenance and support work - per service agreement",
        "is_auto_approved": True
    }
}

response = requests.post(
    'https://api.letsdeel.com/rest/timesheets',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
        'Content-Type': 'application/json'
    },
    json=timesheet_data
)
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const timesheetData = {
  data: {
    contract_id: 'c3f9a1d2',
    quantity: 40,
    date: '2026-02-05',
    description: 'Weekly maintenance and support work - per service agreement',
    is_auto_approved: true
  }
};

const response = await axios.post(
  'https://api.letsdeel.com/rest/timesheets',
  timesheetData,
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
      'Content-Type': 'application/json'
    }
  }
);
```

The timesheet is created with `approved` status immediately, bypassing the manual review workflow.

> **Warning**
>
> Only use auto-approval for pre-authorized work or established contractors with a proven track record. Manual review provides an important control point for verifying hours and work quality.

### Scenario 3: Updating a timesheet before approval

Correct errors in pending timesheets before they are reviewed.

**`Python`**

```python Python
import requests
import os

update_data = {
    "data": {
        "quantity": 38,
        "description": "Backend API development - implemented user authentication endpoints and payment processing integration (corrected hours)"
    }
}

response = requests.patch(
    'https://api.letsdeel.com/rest/timesheets/ts_9876543210',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}',
        'Content-Type': 'application/json'
    },
    json=update_data
)
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const updateData = {
  data: {
    quantity: 38,
    description: 'Backend API development - implemented user authentication endpoints and payment processing integration (corrected hours)'
  }
};

const response = await axios.patch(
  'https://api.letsdeel.com/rest/timesheets/ts_9876543210',
  updateData,
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`,
      'Content-Type': 'application/json'
    }
  }
);
```

> **Note**
>
> You can only update timesheets in `pending` status. Once approved or paid, timesheets cannot be modified. If you need to correct an approved timesheet, contact Deel support or create an adjustment.

### Scenario 4: Listing all timesheets for a contract

Retrieve timesheet history for reporting and tracking purposes.

**`Python`**

```python Python
import requests
import os

response = requests.get(
    'https://api.letsdeel.com/rest/contracts/c3f9a1d2/timesheets',
    headers={
        'Authorization': f'Bearer {os.getenv("DEEL_API_TOKEN")}'
    },
    params={
        'limit': 50,
        'statuses': 'approved,paid'
    }
)

timesheets = response.json()['data']

total_hours = sum(ts['quantity'] for ts in timesheets)
print(f"Total approved/paid hours: {total_hours}")
```

**`Node.js`**

```javascript Node.js
const axios = require('axios');

const response = await axios.get(
  'https://api.letsdeel.com/rest/contracts/c3f9a1d2/timesheets',
  {
    headers: {
      'Authorization': `Bearer ${process.env.DEEL_API_TOKEN}`
    },
    params: {
      limit: 50,
      statuses: 'approved,paid'
    }
  }
);

const timesheets = response.data.data;

const totalHours = timesheets.reduce((sum, ts) => sum + ts.quantity, 0);
console.log(`Total approved/paid hours: ${totalHours}`);
```

This retrieves all approved and paid timesheets for the contract, allowing you to calculate total billable hours.

## Best practices

#### Detailed work descriptions

* **Be specific**: Include what was accomplished, not just general work categories
* **Link to deliverables**: Reference pull requests, tickets, or project milestones when applicable
* **Use consistent formatting**: Establish a description template for your team
* **Avoid vague entries**: "Worked on project" is less useful than "Implemented OAuth2 authentication flow"

#### Timing and submission cadence

* **Submit regularly**: Daily or weekly submissions prevent backlog and ensure accurate time tracking
* **Respect payment cycles**: Submit timesheets early in the cycle to ensure inclusion in the current invoice
* **Track in real-time**: Use time-tracking tools and sync to Deel API to maintain accuracy
* **Review before submission**: Verify hours and descriptions before creating timesheets

#### Preset management

* **Create presets for common work**: Set up presets for recurring work types (development, design, consulting)
* **Use descriptive titles**: Make preset titles clear and specific for easy selection
* **Update rates regularly**: Keep preset rates current with contract amendments
* **Document preset usage**: Maintain internal documentation on when to use each preset

#### Approval workflow

* **Review promptly**: Approve or reject timesheets within 24-48 hours to maintain contractor trust
* **Provide clear feedback**: When rejecting, explain exactly what needs to be corrected
* **Use auto-approval selectively**: Reserve for established contractors with proven accuracy
* **Maintain audit trail**: Include approval reasons for compliance and record-keeping

#### Error handling

* **Validate contract type**: Ensure contracts are time-based before attempting timesheet creation
* **Check scopes**: Verify your API token has required `timesheets:write` and `timesheets:read` scopes
* **Handle rejections gracefully**: Implement retry logic for rate limits and temporary failures
* **Log all operations**: Track timesheet IDs and statuses for debugging and reconciliation

#### Reporting and analytics

* **Track by status**: Regularly query pending timesheets to ensure timely reviews
* **Monitor payment cycles**: Correlate timesheet submission dates with invoice generation
* **Calculate utilization**: Use contract timesheet data to analyze contractor productivity
* **Export for accounting**: Integrate timesheet data with finance systems for reconciliation

## Troubleshooting

#### Timesheet not appearing on invoice

Verify that the timesheet is approved and that the submission date falls within the current payment cycle. If the invoice has already been generated, the timesheet will appear on the next invoice. Check the payment cycle dates using the contracts endpoint.

#### Cannot create timesheet for contract

Timesheets can only be created for `ongoing_time_based` or `fixed_term_time_based` contracts. Verify the contract type using `GET /rest/contracts/{contract_id}`. For fixed-price contracts, use milestones or invoice adjustments instead.

#### Insufficient scope error

Your API token must have `timesheets:write` scope to create or update timesheets, and `timesheets:read` scope to retrieve them. Generate a new token with the required scopes in the Developer Center.

#### Cannot update approved timesheet

Timesheets can only be modified while in `pending` status. Once approved, they are locked for audit purposes. If you need to correct an approved timesheet, contact Deel support or create an invoice adjustment for the difference.

#### Preset not found error

Ensure the `hourly_report_preset_id` exists and belongs to the specified contract. Use `GET /contracts/{contract_id}/timesheets/presets` to list available presets for the contract. Presets are contract-specific and cannot be shared across contracts.

#### Review rejected without reason

When rejecting a timesheet, always include a `reason` field to explain the rejection. This helps the contractor understand what needs to be corrected. If a timesheet was rejected without reason, contact the reviewer or check the timesheet details for any reviewer remarks.

## Next steps

#### [Invoice adjustments](/api/contractors/invoice-adjustments)

Learn how to add bonuses, reimbursements, and other adjustments to invoices

#### [Webhooks](/api/webhooks/introduction)

Set up webhooks to monitor timesheet status changes automatically

#### [Authentication](/api/authentication)

Learn how to authenticate API requests and manage token scopes