Authentication

Authentication, rate limiting, and API conventions for the TaxMTD REST API.

Base URL

TaxMTD provides two API surfaces:

SurfaceBase URLAuth method
Session APIhttps://taxmtd.uk/apiSession cookies
Public API (v1)https://taxmtd.uk/api/v1API key (Bearer token)

The Session API is used by the TaxMTD web app and requires login cookies. The Public API is designed for external integrations, scripts, and third-party apps using API keys.

API keys provide stateless, token-based access to the Public API. Generate keys from Settings → API Keys in your TaxMTD dashboard.

Using Your API Key

Pass the key as a Bearer token in the Authorization header:

JavaScript
const data = await fetch('https://taxmtd.uk/api/v1/transactions', {
  headers: { 'Authorization': 'Bearer tmtd_your_api_key_here' }
}).then(r => r.json())
Python
import requests

headers = {'Authorization': 'Bearer tmtd_your_api_key_here'}
r = requests.get('https://taxmtd.uk/api/v1/transactions', headers=headers)
data = r.json()
PHP
$response = Http::withToken('tmtd_your_api_key_here')
    ->get('https://taxmtd.uk/api/v1/transactions');
$data = $response->json();
Rust
let res = reqwest::Client::new()
    .get("https://taxmtd.uk/api/v1/transactions")
    .bearer_auth("tmtd_your_api_key_here")
    .send().await?
    .json::<serde_json::Value>().await?;
cURL
curl https://taxmtd.uk/api/v1/transactions \
  -H "Authorization: Bearer tmtd_your_api_key_here"

Available v1 Endpoints

MethodEndpointDescription
GET/api/v1/transactionsList transactions
GET/api/v1/invoicesList invoices with line items
POST/api/v1/invoicesCreate invoice
GET/api/v1/billsList bills with line items
POST/api/v1/billsCreate bill
GET/api/v1/contactsList contacts
POST/api/v1/contactsCreate contact
GET/api/v1/productsList products
POST/api/v1/productsCreate product
GET/api/v1/employeesList employees
GET/api/v1/categoriesList expense categories

Pagination

All GET endpoints support limit and offset query parameters:

curl "https://taxmtd.uk/api/v1/transactions?limit=25&offset=50" \
  -H "Authorization: Bearer tmtd_your_api_key_here"
  • limit - Number of records to return (default 50, max 200)
  • offset - Number of records to skip (default 0)

API Key Scoping

API keys can optionally be scoped to a specific entity (business). When scoped, all queries automatically filter to that entity's data. Unscoped keys return data across all entities owned by the key creator.

Managing API Keys

JavaScript (Nuxt)
// List keys (full key is never returned)
const keys = await $fetch('https://taxmtd.uk/api/api-keys')

// Create a new key
const { data } = await $fetch('https://taxmtd.uk/api/api-keys', {
  method: 'POST',
  body: { name: 'My Integration', entity_id: '...' }
})
// data.raw_key is shown ONCE - save it immediately

// Revoke a key
await $fetch('https://taxmtd.uk/api/api-keys', {
  method: 'DELETE',
  body: { id: 'key-uuid' }
})
Python
import requests

# List keys
keys = requests.get(
    "https://taxmtd.uk/api/api-keys",
    cookies=session_cookies,
).json()

# Create a new key
res = requests.post(
    "https://taxmtd.uk/api/api-keys",
    json={"name": "My Integration", "entity_id": "..."},
    cookies=session_cookies,
)
raw_key = res.json()["data"]["raw_key"]  # shown ONCE

# Revoke a key
requests.delete(
    "https://taxmtd.uk/api/api-keys",
    json={"id": "key-uuid"},
    cookies=session_cookies,
)
PHP
// List keys
$keys = Http::withCookies($session)
    ->get('https://taxmtd.uk/api/api-keys')
    ->json();

// Create a new key
$response = Http::withCookies($session)->post(
    'https://taxmtd.uk/api/api-keys',
    ['name' => 'My Integration', 'entity_id' => '...']
);
$rawKey = $response->json()['data']['raw_key']; // shown ONCE

// Revoke a key
Http::withCookies($session)->delete(
    'https://taxmtd.uk/api/api-keys',
    ['id' => 'key-uuid']
);
Rust
// List keys
let keys: serde_json::Value = client
    .get("https://taxmtd.uk/api/api-keys")
    .send().await?
    .json().await?;

// Create a new key
let res: serde_json::Value = client
    .post("https://taxmtd.uk/api/api-keys")
    .json(&serde_json::json!({
        "name": "My Integration",
        "entity_id": "..."
    }))
    .send().await?
    .json().await?;
let raw_key = &res["data"]["raw_key"]; // shown ONCE

// Revoke a key
client.delete("https://taxmtd.uk/api/api-keys")
    .json(&serde_json::json!({ "id": "key-uuid" }))
    .send().await?;
cURL
# Create a new key
curl -X POST https://taxmtd.uk/api/api-keys \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"name":"My Integration"}'

# Revoke a key
curl -X DELETE https://taxmtd.uk/api/api-keys \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"id":"key-uuid"}'
API keys are shown once when created. Store the key securely - it cannot be retrieved again. If lost, revoke and create a new one.

Session Authentication

The session API is used internally by the TaxMTD web app. If you're building a browser-based integration that shares the same domain, you can use session cookies.

Obtaining a Session

JavaScript (Nuxt)
// Using $fetch (auto-includes cookies)
const data = await $fetch('https://taxmtd.uk/api/transactions')
JavaScript (External)
// Login first
const session = await fetch('https://taxmtd.uk/api/auth/login', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'user@example.com', password: '...' })
})
// Subsequent requests include cookies automatically
const data = await fetch('https://taxmtd.uk/api/transactions', {
  credentials: 'include'
})
Python
import requests

session = requests.Session()
session.post(
    "https://taxmtd.uk/api/auth/login",
    json={"email": "user@example.com", "password": "..."},
)
# Subsequent requests include cookies automatically
res = session.get("https://taxmtd.uk/api/transactions")
data = res.json()["data"]
PHP
$client = new GuzzleHttp\Client(['cookies' => true]);
$client->post('https://taxmtd.uk/api/auth/login', [
    'json' => ['email' => 'user@example.com', 'password' => '...']
]);
$response = $client->get('https://taxmtd.uk/api/transactions');
Rust
let client = reqwest::Client::builder()
    .cookie_store(true)
    .build()?;

// Login
client.post("https://taxmtd.uk/api/auth/login")
    .json(&serde_json::json!({
        "email": "user@example.com",
        "password": "..."
    }))
    .send().await?;

// Subsequent requests include cookies automatically
let data: serde_json::Value = client
    .get("https://taxmtd.uk/api/transactions")
    .send().await?
    .json().await?;
cURL
# Login and save cookies
curl -c cookies.txt -X POST https://taxmtd.uk/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"..."}'

# Use saved cookies for requests
curl -b cookies.txt https://taxmtd.uk/api/transactions

Response Format

All endpoints return JSON. Successful responses:

{
  "data": [...],
  "limit": 50,
  "offset": 0
}

Error responses:

{
  "statusCode": 400,
  "statusMessage": "Bad Request",
  "message": "Missing required field: periodId"
}

HTTP Status Codes

CodeMeaning
200Success
201Created
400Bad request - invalid parameters
401Unauthorised - no session or invalid API key
404Not found
405Method not allowed
500Server error

Rate Limiting

API key requests are rate-limited to 60 requests per minute per endpoint per key. Session API requests have per-endpoint limits documented on each endpoint page.

Content Types

MethodContent-Type
GETQuery parameters
POSTapplication/json
PUTapplication/json
DELETEapplication/json