Mileage, Receipts & Time

API reference for mileage logging, receipt capture, time entries, and project management.

Mileage

List Mileage Entries

JavaScript
const entries = await $fetch('https://taxmtd.uk/api/mileage')
Python
res = requests.get(
    "https://taxmtd.uk/api/mileage",
    cookies=session_cookies,
)
entries = res.json()["data"]
PHP
$response = Http::withCookies($session)
    ->get('https://taxmtd.uk/api/mileage');

$entries = $response->json()['data'];
Rust
#[derive(Deserialize)]
struct MileageEntry {
    id: i64,
    date: String,
    from: String,
    to: String,
    miles: f64,
    purpose: String,
    vehicle_type: String,
}

let entries: Vec<MileageEntry> = client
    .get("https://taxmtd.uk/api/mileage")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .as_array().unwrap()
    .iter()
    .map(|v| serde_json::from_value(v.clone()).unwrap())
    .collect();
cURL
curl https://taxmtd.uk/api/mileage

Log Mileage

JavaScript
await $fetch('https://taxmtd.uk/api/mileage', {
  method: 'POST',
  body: {
    date: '2026-03-01',
    from: 'London',
    to: 'Manchester',
    miles: 200,
    purpose: 'Client meeting',
    vehicleType: 'car'  // car, motorcycle, bicycle
  }
})
Python
res = requests.post(
    "https://taxmtd.uk/api/mileage",
    json={
        "date": "2026-03-01",
        "from": "London",
        "to": "Manchester",
        "miles": 200,
        "purpose": "Client meeting",
        "vehicleType": "car",
    },
    cookies=session_cookies,
)
data = res.json()["data"]
PHP
$response = Http::withCookies($session)->post(
    'https://taxmtd.uk/api/mileage',
    [
        'date' => '2026-03-01',
        'from' => 'London',
        'to' => 'Manchester',
        'miles' => 200,
        'purpose' => 'Client meeting',
        'vehicleType' => 'car',
    ]
);

$data = $response->json()['data'];
Rust
let res = client.post("https://taxmtd.uk/api/mileage")
    .json(&serde_json::json!({
        "date": "2026-03-01",
        "from": "London",
        "to": "Manchester",
        "miles": 200,
        "purpose": "Client meeting",
        "vehicleType": "car"
    }))
    .send().await?;
cURL
curl -X POST https://taxmtd.uk/api/mileage \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-03-01","from":"London","to":"Manchester","miles":200,"purpose":"Client meeting"}'

Update & Delete

JavaScript
// Update
await $fetch('https://taxmtd.uk/api/mileage', {
  method: 'PUT',
  body: { id: 1, miles: 210, purpose: 'Client meeting (return)' }
})

// Delete
await $fetch('https://taxmtd.uk/api/mileage', {
  method: 'DELETE',
  body: { id: 1 }
})
Python
# Update
res = requests.put(
    "https://taxmtd.uk/api/mileage",
    json={"id": 1, "miles": 210, "purpose": "Client meeting (return)"},
    cookies=session_cookies,
)
data = res.json()["data"]

# Delete
requests.delete(
    "https://taxmtd.uk/api/mileage",
    json={"id": 1},
    cookies=session_cookies,
)
PHP
// Update
$response = Http::withCookies($session)->put(
    'https://taxmtd.uk/api/mileage',
    ['id' => 1, 'miles' => 210, 'purpose' => 'Client meeting (return)']
);

$data = $response->json()['data'];

// Delete
Http::withCookies($session)->delete(
    'https://taxmtd.uk/api/mileage',
    ['id' => 1]
);
Rust
// Update
let res = client.put("https://taxmtd.uk/api/mileage")
    .json(&serde_json::json!({
        "id": 1,
        "miles": 210,
        "purpose": "Client meeting (return)"
    }))
    .send().await?;

// Delete
let res = client.delete("https://taxmtd.uk/api/mileage")
    .json(&serde_json::json!({ "id": 1 }))
    .send().await?;
cURL
# Update
curl -X PUT https://taxmtd.uk/api/mileage \
  -H "Content-Type: application/json" \
  -d '{"id":1,"miles":210}'

# Delete
curl -X DELETE https://taxmtd.uk/api/mileage \
  -H "Content-Type: application/json" \
  -d '{"id":1}'

HMRC Mileage Rates

VehicleFirst 10,000 milesOver 10,000
Car/van45p/mile25p/mile
Motorcycle24p/mile24p/mile
Bicycle20p/mile20p/mile

Receipts

List Receipts

JavaScript
const receipts = await $fetch('https://taxmtd.uk/api/receipts')
Python
res = requests.get(
    "https://taxmtd.uk/api/receipts",
    cookies=session_cookies,
)
receipts = res.json()["data"]
PHP
$response = Http::withCookies($session)
    ->get('https://taxmtd.uk/api/receipts');

$receipts = $response->json()['data'];
Rust
#[derive(Deserialize)]
struct Receipt {
    id: i64,
    merchant: String,
    amount: f64,
    date: String,
    category: Option<String>,
}

let receipts: Vec<Receipt> = client
    .get("https://taxmtd.uk/api/receipts")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .as_array().unwrap()
    .iter()
    .map(|v| serde_json::from_value(v.clone()).unwrap())
    .collect();
cURL
curl https://taxmtd.uk/api/receipts

Upload Receipt

JavaScript
await $fetch('https://taxmtd.uk/api/receipts', {
  method: 'POST',
  body: {
    image: 'base64-encoded-image...',
    merchant: 'Staples',
    amount: 45.99,
    date: '2026-03-01',
    category: 'office-equipment',
    transactionId: 42  // optional: link to transaction
  }
})
Python
res = requests.post(
    "https://taxmtd.uk/api/receipts",
    json={
        "image": "base64-encoded-image...",
        "merchant": "Staples",
        "amount": 45.99,
        "date": "2026-03-01",
        "category": "office-equipment",
        "transactionId": 42,
    },
    cookies=session_cookies,
)
data = res.json()["data"]
PHP
$response = Http::withCookies($session)->post(
    'https://taxmtd.uk/api/receipts',
    [
        'image' => 'base64-encoded-image...',
        'merchant' => 'Staples',
        'amount' => 45.99,
        'date' => '2026-03-01',
        'category' => 'office-equipment',
        'transactionId' => 42,
    ]
);

$data = $response->json()['data'];
Rust
let res = client.post("https://taxmtd.uk/api/receipts")
    .json(&serde_json::json!({
        "image": "base64-encoded-image...",
        "merchant": "Staples",
        "amount": 45.99,
        "date": "2026-03-01",
        "category": "office-equipment",
        "transactionId": 42
    }))
    .send().await?;
cURL
curl -X POST https://taxmtd.uk/api/receipts \
  -H "Content-Type: application/json" \
  -d '{"merchant":"Staples","amount":45.99,"date":"2026-03-01"}'

Projects

CRUD Operations

JavaScript
// List
const projects = await $fetch('https://taxmtd.uk/api/projects')

// Create
await $fetch('https://taxmtd.uk/api/projects', {
  method: 'POST',
  body: { name: 'Website Redesign', client: 'Acme Ltd', hourlyRate: 75 }
})

// Update
await $fetch('https://taxmtd.uk/api/projects', {
  method: 'PUT',
  body: { id: 1, status: 'completed' }
})

// Delete
await $fetch('https://taxmtd.uk/api/projects', {
  method: 'DELETE',
  body: { id: 1 }
})
Python
# List
res = requests.get(
    "https://taxmtd.uk/api/projects",
    cookies=session_cookies,
)
projects = res.json()["data"]

# Create
res = requests.post(
    "https://taxmtd.uk/api/projects",
    json={"name": "Website Redesign", "client": "Acme Ltd", "hourlyRate": 75},
    cookies=session_cookies,
)

# Update
requests.put(
    "https://taxmtd.uk/api/projects",
    json={"id": 1, "status": "completed"},
    cookies=session_cookies,
)

# Delete
requests.delete(
    "https://taxmtd.uk/api/projects",
    json={"id": 1},
    cookies=session_cookies,
)
PHP
// List
$projects = Http::withCookies($session)
    ->get('https://taxmtd.uk/api/projects')
    ->json()['data'];

// Create
Http::withCookies($session)->post(
    'https://taxmtd.uk/api/projects',
    ['name' => 'Website Redesign', 'client' => 'Acme Ltd', 'hourlyRate' => 75]
);

// Update
Http::withCookies($session)->put(
    'https://taxmtd.uk/api/projects',
    ['id' => 1, 'status' => 'completed']
);

// Delete
Http::withCookies($session)->delete(
    'https://taxmtd.uk/api/projects',
    ['id' => 1]
);
Rust
// List
let projects: serde_json::Value = client
    .get("https://taxmtd.uk/api/projects")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();

// Create
client.post("https://taxmtd.uk/api/projects")
    .json(&serde_json::json!({
        "name": "Website Redesign",
        "client": "Acme Ltd",
        "hourlyRate": 75
    }))
    .send().await?;

// Update
client.put("https://taxmtd.uk/api/projects")
    .json(&serde_json::json!({ "id": 1, "status": "completed" }))
    .send().await?;

// Delete
client.delete("https://taxmtd.uk/api/projects")
    .json(&serde_json::json!({ "id": 1 }))
    .send().await?;
cURL
curl https://taxmtd.uk/api/projects
curl -X POST https://taxmtd.uk/api/projects \
  -H "Content-Type: application/json" \
  -d '{"name":"Website Redesign","client":"Acme Ltd","hourlyRate":75}'

Time Entries

CRUD Operations

JavaScript
// List
const entries = await $fetch('https://taxmtd.uk/api/time-entries')

// Create
await $fetch('https://taxmtd.uk/api/time-entries', {
  method: 'POST',
  body: {
    projectId: 1,
    date: '2026-03-01',
    duration: 3.5,
    description: 'Frontend development',
    billable: true
  }
})

// Update
await $fetch('https://taxmtd.uk/api/time-entries', {
  method: 'PUT',
  body: { id: 1, duration: 4.0 }
})

// Delete
await $fetch('https://taxmtd.uk/api/time-entries', {
  method: 'DELETE',
  body: { id: 1 }
})
Python
# List
res = requests.get(
    "https://taxmtd.uk/api/time-entries",
    cookies=session_cookies,
)
entries = res.json()["data"]

# Create
res = requests.post(
    "https://taxmtd.uk/api/time-entries",
    json={
        "projectId": 1,
        "date": "2026-03-01",
        "duration": 3.5,
        "description": "Frontend development",
        "billable": True,
    },
    cookies=session_cookies,
)

# Update
requests.put(
    "https://taxmtd.uk/api/time-entries",
    json={"id": 1, "duration": 4.0},
    cookies=session_cookies,
)

# Delete
requests.delete(
    "https://taxmtd.uk/api/time-entries",
    json={"id": 1},
    cookies=session_cookies,
)
PHP
// List
$entries = Http::withCookies($session)
    ->get('https://taxmtd.uk/api/time-entries')
    ->json()['data'];

// Create
Http::withCookies($session)->post(
    'https://taxmtd.uk/api/time-entries',
    [
        'projectId' => 1,
        'date' => '2026-03-01',
        'duration' => 3.5,
        'description' => 'Frontend development',
        'billable' => true,
    ]
);

// Update
Http::withCookies($session)->put(
    'https://taxmtd.uk/api/time-entries',
    ['id' => 1, 'duration' => 4.0]
);

// Delete
Http::withCookies($session)->delete(
    'https://taxmtd.uk/api/time-entries',
    ['id' => 1]
);
Rust
// List
let entries: serde_json::Value = client
    .get("https://taxmtd.uk/api/time-entries")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();

// Create
client.post("https://taxmtd.uk/api/time-entries")
    .json(&serde_json::json!({
        "projectId": 1,
        "date": "2026-03-01",
        "duration": 3.5,
        "description": "Frontend development",
        "billable": true
    }))
    .send().await?;

// Update
client.put("https://taxmtd.uk/api/time-entries")
    .json(&serde_json::json!({ "id": 1, "duration": 4.0 }))
    .send().await?;

// Delete
client.delete("https://taxmtd.uk/api/time-entries")
    .json(&serde_json::json!({ "id": 1 }))
    .send().await?;
cURL
# List time entries
curl https://taxmtd.uk/api/time-entries

# Create time entry
curl -X POST https://taxmtd.uk/api/time-entries \
  -H "Content-Type: application/json" \
  -d '{"projectId":1,"date":"2026-03-01","duration":3.5,"description":"Frontend dev","billable":true}'

Timing App Integration

Import time entries from the macOS Timing app via Web API or CSV file export.

Connect (Web API)

JavaScript
// Connect via API (bearer token from web.timingapp.com → API Keys)
await $fetch('https://taxmtd.uk/api/timing/connect', {
  method: 'POST',
  body: { apiToken: 'tmng_your_token_here' }
})
cURL
curl -X POST https://taxmtd.uk/api/timing/connect \
  -H "Content-Type: application/json" \
  -d '{"apiToken":"tmng_your_token_here"}'

Import Entries

JavaScript
// Import all entries
const result = await $fetch('https://taxmtd.uk/api/timing/import', {
  method: 'POST'
})
// → { imported: 47, skipped: 0, projectsCreated: 5, projectsMapped: 0 }

// Import with date range
await $fetch('https://taxmtd.uk/api/timing/import', {
  method: 'POST',
  body: { startDate: '2026-01-01', endDate: '2026-03-31' }
})
Python
# Import all entries
res = requests.post(
    "https://taxmtd.uk/api/timing/import",
    cookies=session_cookies,
)
result = res.json()["data"]
# → { imported: 47, skipped: 0, projectsCreated: 5, projectsMapped: 0 }

# Import with date range
res = requests.post(
    "https://taxmtd.uk/api/timing/import",
    json={"startDate": "2026-01-01", "endDate": "2026-03-31"},
    cookies=session_cookies,
)
PHP
// Import all entries
$response = Http::withCookies($session)
    ->post('https://taxmtd.uk/api/timing/import');

$result = $response->json()['data'];

// Import with date range
Http::withCookies($session)->post(
    'https://taxmtd.uk/api/timing/import',
    ['startDate' => '2026-01-01', 'endDate' => '2026-03-31']
);
Rust
// Import all entries
let result: serde_json::Value = client
    .post("https://taxmtd.uk/api/timing/import")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();

// Import with date range
client.post("https://taxmtd.uk/api/timing/import")
    .json(&serde_json::json!({
        "startDate": "2026-01-01",
        "endDate": "2026-03-31"
    }))
    .send().await?;
cURL
curl -X POST https://taxmtd.uk/api/timing/import \
  -H "Content-Type: application/json" \
  -d '{"startDate":"2026-01-01","endDate":"2026-03-31"}'

List Timing Projects

JavaScript
const projects = await $fetch('https://taxmtd.uk/api/timing/projects')
// → [{ id: '42', title: 'Client Work', parentTitle: null, color: '#ff0000' }]
Python
res = requests.get(
    "https://taxmtd.uk/api/timing/projects",
    cookies=session_cookies,
)
projects = res.json()["data"]
PHP
$response = Http::withCookies($session)
    ->get('https://taxmtd.uk/api/timing/projects');

$projects = $response->json()['data'];
Rust
let projects: serde_json::Value = client
    .get("https://taxmtd.uk/api/timing/projects")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();
cURL
curl https://taxmtd.uk/api/timing/projects

Disconnect

JavaScript
await $fetch('https://taxmtd.uk/api/timing/disconnect', { method: 'POST' })
Python
res = requests.post(
    "https://taxmtd.uk/api/timing/disconnect",
    cookies=session_cookies,
)
PHP
Http::withCookies($session)
    ->post('https://taxmtd.uk/api/timing/disconnect');
Rust
client.post("https://taxmtd.uk/api/timing/disconnect")
    .send().await?;
cURL
curl -X POST https://taxmtd.uk/api/timing/disconnect

Upload CSV

For users who don't sync to Timing's cloud - export from the app (File → Export Report as CSV), then upload.

JavaScript
const csvContent = '...' // Read from file
const result = await $fetch('https://taxmtd.uk/api/timing/upload', {
  method: 'POST',
  body: { csv: csvContent }
})
// → { imported: 23, skipped: 0, projectsCreated: 3, projectsMapped: 0 }
cURL
curl -X POST https://taxmtd.uk/api/timing/upload \
  -H "Content-Type: application/json" \
  -d '{"csv":"Project,Title,Start Date,Duration\nClient Work,Design review,2026-03-01,1.5"}'