Authentication
Base URL
TaxMTD provides two API surfaces:
| Surface | Base URL | Auth method |
|---|---|---|
| Session API | https://dev.taxmtd.uk/api | Session cookies |
| Public API (v1) | https://dev.taxmtd.uk/api/v1 | API 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 Key Authentication (Recommended)
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:
const data = await fetch('https://dev.taxmtd.uk/api/v1/transactions', {
headers: { 'Authorization': 'Bearer tmtd_your_api_key_here' }
}).then(r => r.json())
import requests
headers = {'Authorization': 'Bearer tmtd_your_api_key_here'}
r = requests.get('https://dev.taxmtd.uk/api/v1/transactions', headers=headers)
data = r.json()
$response = Http::withToken('tmtd_your_api_key_here')
->get('https://dev.taxmtd.uk/api/v1/transactions');
$data = $response->json();
let res = reqwest::Client::new()
.get("https://dev.taxmtd.uk/api/v1/transactions")
.bearer_auth("tmtd_your_api_key_here")
.send().await?
.json::<serde_json::Value>().await?;
curl https://dev.taxmtd.uk/api/v1/transactions \
-H "Authorization: Bearer tmtd_your_api_key_here"
Available v1 Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/transactions | List transactions |
GET | /api/v1/invoices | List invoices with line items |
POST | /api/v1/invoices | Create invoice |
GET | /api/v1/bills | List bills with line items |
POST | /api/v1/bills | Create bill |
GET | /api/v1/contacts | List contacts |
POST | /api/v1/contacts | Create contact |
GET | /api/v1/products | List products |
POST | /api/v1/products | Create product |
GET | /api/v1/employees | List employees |
GET | /api/v1/categories | List expense categories |
Pagination
All GET endpoints support limit and offset query parameters:
curl "https://dev.taxmtd.uk/api/v1/transactions?limit=25&offset=50" \
-H "Authorization: Bearer tmtd_your_api_key_here"
limit- Number of records to return (default50, max200)offset- Number of records to skip (default0)
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
// List keys (full key is never returned)
const keys = await $fetch('https://dev.taxmtd.uk/api/api-keys')
// Create a new key
const { data } = await $fetch('https://dev.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://dev.taxmtd.uk/api/api-keys', {
method: 'DELETE',
body: { id: 'key-uuid' }
})
import requests
# List keys
keys = requests.get(
"https://dev.taxmtd.uk/api/api-keys",
cookies=session_cookies,
).json()
# Create a new key
res = requests.post(
"https://dev.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://dev.taxmtd.uk/api/api-keys",
json={"id": "key-uuid"},
cookies=session_cookies,
)
// List keys
$keys = Http::withCookies($session)
->get('https://dev.taxmtd.uk/api/api-keys')
->json();
// Create a new key
$response = Http::withCookies($session)->post(
'https://dev.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://dev.taxmtd.uk/api/api-keys',
['id' => 'key-uuid']
);
// List keys
let keys: serde_json::Value = client
.get("https://dev.taxmtd.uk/api/api-keys")
.send().await?
.json().await?;
// Create a new key
let res: serde_json::Value = client
.post("https://dev.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://dev.taxmtd.uk/api/api-keys")
.json(&serde_json::json!({ "id": "key-uuid" }))
.send().await?;
# Create a new key
curl -X POST https://dev.taxmtd.uk/api/api-keys \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{"name":"My Integration"}'
# Revoke a key
curl -X DELETE https://dev.taxmtd.uk/api/api-keys \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{"id":"key-uuid"}'
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
// Using $fetch (auto-includes cookies)
const data = await $fetch('https://dev.taxmtd.uk/api/transactions')
// Login first
const session = await fetch('https://dev.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://dev.taxmtd.uk/api/transactions', {
credentials: 'include'
})
import requests
session = requests.Session()
session.post(
"https://dev.taxmtd.uk/api/auth/login",
json={"email": "user@example.com", "password": "..."},
)
# Subsequent requests include cookies automatically
res = session.get("https://dev.taxmtd.uk/api/transactions")
data = res.json()["data"]
$client = new GuzzleHttp\Client(['cookies' => true]);
$client->post('https://dev.taxmtd.uk/api/auth/login', [
'json' => ['email' => 'user@example.com', 'password' => '...']
]);
$response = $client->get('https://dev.taxmtd.uk/api/transactions');
let client = reqwest::Client::builder()
.cookie_store(true)
.build()?;
// Login
client.post("https://dev.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://dev.taxmtd.uk/api/transactions")
.send().await?
.json().await?;
# Login and save cookies
curl -c cookies.txt -X POST https://dev.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://dev.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
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad request - invalid parameters |
401 | Unauthorised - no session or invalid API key |
404 | Not found |
405 | Method not allowed |
500 | Server 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
| Method | Content-Type |
|---|---|
| GET | Query parameters |
| POST | application/json |
| PUT | application/json |
| DELETE | application/json |