HMRC & Tax

API reference for HMRC integration, SA103 data, and tax submissions.

HMRC Status

Check whether HMRC is connected and tokens are valid.

JavaScript
const status = await $fetch('https://dev.taxmtd.uk/api/hmrc/status')
// Returns: { connected: boolean, nino: string | null, expiresAt: string | null }
Python
res = requests.get(
    "https://dev.taxmtd.uk/api/hmrc/status",
    cookies=session_cookies,
)
status = res.json()["data"]
connected = status["connected"]
PHP
$response = Http::withCookies($session)
    ->get('https://dev.taxmtd.uk/api/hmrc/status');

$status = $response->json()['data'];
$connected = $status['connected'];
Rust
#[derive(Deserialize)]
struct HmrcStatus {
    connected: bool,
    nino: Option<String>,
    expires_at: Option<String>,
}

let status: HmrcStatus = client
    .get("https://dev.taxmtd.uk/api/hmrc/status")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone()
    .pipe(serde_json::from_value)?;
cURL
curl https://dev.taxmtd.uk/api/hmrc/status

Store NINO

Save the user's National Insurance Number for HMRC submissions.

JavaScript
await $fetch('https://dev.taxmtd.uk/api/hmrc/nino', {
  method: 'POST',
  body: { nino: 'QQ123456C' }
})
Python
res = requests.post(
    "https://dev.taxmtd.uk/api/hmrc/nino",
    json={"nino": "QQ123456C"},
    cookies=session_cookies,
)
data = res.json()["data"]
PHP
$response = Http::withCookies($session)->post(
    'https://dev.taxmtd.uk/api/hmrc/nino',
    ['nino' => 'QQ123456C']
);

$data = $response->json()['data'];
Rust
let res = client.post("https://dev.taxmtd.uk/api/hmrc/nino")
    .json(&serde_json::json!({ "nino": "QQ123456C" }))
    .send().await?;
cURL
curl -X POST https://dev.taxmtd.uk/api/hmrc/nino \
  -H "Content-Type: application/json" \
  -d '{"nino":"QQ123456C"}'

Initiate HMRC OAuth

Redirects the user to HMRC Gov Gateway for authorisation.

JavaScript
// Server-side redirect to HMRC
const { url } = await $fetch('https://dev.taxmtd.uk/api/hmrc/auth')
navigateTo(url, { external: true })

// After authorisation, HMRC redirects to /api/hmrc/callback
// Tokens are stored automatically
Python
res = requests.get(
    "https://dev.taxmtd.uk/api/hmrc/auth",
    cookies=session_cookies,
)
url = res.json()["data"]["url"]
# Redirect the user to `url` in the browser
PHP
$response = Http::withCookies($session)
    ->get('https://dev.taxmtd.uk/api/hmrc/auth');

$url = $response->json()['data']['url'];
// Redirect the user to $url in the browser
Rust
let res: serde_json::Value = client
    .get("https://dev.taxmtd.uk/api/hmrc/auth")
    .send().await?
    .json::<serde_json::Value>().await?;
let url = res["data"]["url"].as_str().unwrap();
// Redirect the user to `url` in the browser
cURL
# Get the OAuth URL
curl https://dev.taxmtd.uk/api/hmrc/auth
# Visit the returned URL in browser

Get SA103 Data

Retrieve the auto-generated Self-Employment supplement data.

JavaScript
const sa103 = await $fetch('https://dev.taxmtd.uk/api/hmrc/sa103')
// Returns:
// {
//   turnover: 42000,
//   boxes: {
//     box16: 1200,   // Cost of goods
//     box17: 0,      // CIS
//     box18: 0,      // Staff
//     box19: 2400,   // Premises
//     box20: 150,    // Repairs
//     box21: 890,    // Admin
//     box22: 1200,   // Motor
//     box23: 450,    // Travel
//     box24: 300,    // Advertising
//     box25: 120,    // Interest
//     box26: 600,    // Accountancy
//     box27: 250,    // Other
//   },
//   totalExpenses: 7560,
//   netProfit: 34440
// }
Python
res = requests.get(
    "https://dev.taxmtd.uk/api/hmrc/sa103",
    cookies=session_cookies,
)
sa103 = res.json()["data"]
print(f"Turnover: £{sa103['turnover']}")
print(f"Net Profit: £{sa103['netProfit']}")

for box, value in sa103["boxes"].items():
    if value > 0:
        print(f"{box}: £{value}")
PHP
$response = Http::withCookies($session)
    ->get('https://dev.taxmtd.uk/api/hmrc/sa103');

$sa103 = $response->json()['data'];
echo "Turnover: £{$sa103['turnover']}\n";
echo "Net Profit: £{$sa103['netProfit']}\n";

foreach ($sa103['boxes'] as $box => $value) {
    if ($value > 0) {
        echo "{$box}: £{$value}\n";
    }
}
Rust
let sa103: serde_json::Value = client
    .get("https://dev.taxmtd.uk/api/hmrc/sa103")
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();
println!("Net Profit: £{}", sa103["netProfit"]);
cURL
curl https://dev.taxmtd.uk/api/hmrc/sa103

National Insurance Annual Summary

Fetches the HMRC-authoritative Class 1 NICable earnings and Class 2 contributions for the active entity's UTR and the requested tax year. Backs on to GET /national-insurance/sa/{utr}/annual-summary/{taxYear}.

Requires an HMRC-connected session and an entity with a UTR. The OAuth scope read:national-insurance is requested automatically at the connect step.

JavaScript
const ni = await $fetch('https://dev.taxmtd.uk/api/hmrc/ni', {
  query: { entityId: 'entity-uuid', taxYear: '2025' } // taxYear optional
})
// Returns:
// {
//   entityId, entityName, utr,
//   taxYear: '2025-26',
//   class1NICableEarnings: 42000,
//   class2Due: 179.40,
//   maxNICsReached: false,
//   fetchedAt: '...',
//   notFound?: true        // HMRC has no record for this year yet
// }
Python
res = requests.get(
    "https://dev.taxmtd.uk/api/hmrc/ni",
    params={"entityId": "entity-uuid", "taxYear": "2025"},
    cookies=session_cookies,
)
ni = res.json()["data"]
print(f"Class 2 due: £{ni['class2Due']}")
PHP
$response = Http::withCookies($session)
    ->get('https://dev.taxmtd.uk/api/hmrc/ni', [
        'entityId' => 'entity-uuid',
        'taxYear'  => '2025',
    ]);

$ni = $response->json()['data'];
Rust
let ni: serde_json::Value = client
    .get("https://dev.taxmtd.uk/api/hmrc/ni")
    .query(&[("entityId", "entity-uuid"), ("taxYear", "2025")])
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();
cURL
curl "https://dev.taxmtd.uk/api/hmrc/ni?entityId=entity-uuid&taxYear=2025"

P&L Report

JavaScript
const pnl = await $fetch('https://dev.taxmtd.uk/api/reports/pnl', {
  params: { periodId: 1 }
})
// Returns: { totalIncome, totalExpenses, netProfit, categories: {...} }
Python
res = requests.get(
    "https://dev.taxmtd.uk/api/reports/pnl",
    params={"periodId": 1},
    cookies=session_cookies,
)
pnl = res.json()["data"]
print(f"Net Profit: £{pnl['netProfit']}")
PHP
$response = Http::withCookies($session)
    ->get('https://dev.taxmtd.uk/api/reports/pnl', ['periodId' => 1]);

$pnl = $response->json()['data'];
Rust
let pnl: serde_json::Value = client
    .get("https://dev.taxmtd.uk/api/reports/pnl")
    .query(&[("periodId", "1")])
    .send().await?
    .json::<serde_json::Value>().await?["data"]
    .clone();
println!("Net Profit: £{}", pnl["netProfit"]);
cURL
curl "https://dev.taxmtd.uk/api/reports/pnl?periodId=1"

Disconnect HMRC

JavaScript
await $fetch('https://dev.taxmtd.uk/api/hmrc/disconnect', {
  method: 'POST'
})
Python
res = requests.post(
    "https://dev.taxmtd.uk/api/hmrc/disconnect",
    cookies=session_cookies,
)
data = res.json()["data"]
PHP
$response = Http::withCookies($session)
    ->post('https://dev.taxmtd.uk/api/hmrc/disconnect');

$data = $response->json()['data'];
Rust
let res = client
    .post("https://dev.taxmtd.uk/api/hmrc/disconnect")
    .send().await?;
cURL
curl -X POST https://dev.taxmtd.uk/api/hmrc/disconnect