The diya-gl format
A year of accounts in two text files, and the mapping, the checks and the evidence behind them.
What diya-gl is
diya-gl is the file format behind DIY Accounting's books pages, its command line tools and the spreadsheets it generates. A whole
year of a small business's accounts is two text files in a zip. book.toml says who the business is, what period the
books cover and what the chart of accounts holds. lines.jsonl holds one transaction per line. A sole trader's year
runs to about 15 KB.
It is for people who want to own their accounting data outright. You can read it in a text editor, keep it in git and see what changed, and recalculate it in a browser or on a command line without Excel and without a server. This page is the specification: the fields, the figures they compute, the checks they have to pass, the zip they travel in, and the evidence that the figures agree with the spreadsheets we have shipped for twenty years.
The declared subset
Every field name in the two schemas is taken from the XBRL Global Ledger Taxonomy Framework 2015, the XBRL Standards Board's recommendation of 25 March 2015. The tables below name the element each field comes from.
That is a lineage and a published mapping, and this page claims nothing past what the tables show. XBRL GL runs no conformance programme and issues no certificate for a data format. A diya-gl file is TOML and JSON Lines; it is not an XBRL instance document, and no XBRL processor reads it.
The format names 48 elements, from four of the framework's modules: gl-cor (core),
gl-bus (business), gl-muc (multicurrency) and gl-taf (tax audit file). No other element of
the framework is named in either schema. The framework's nested tuples are flattened: an entry header and its detail are one line,
and gl-cor:account becomes the pair accountMainID and accountMainDescription. Where a UK
small business needs something the framework has no element for, the schema declares a diya-gl extension: a
diya-gl: prefix on a field, or a whole table marked as one. There are 135 extension fields, and the tables
below mark every one.
The schemas themselves are published beside this page: diya-gl-book-v2.schema.json and diya-gl-lines-v2.schema.json, both JSON Schema draft 2020-12. A longer commentary on the mapping is in diya-gl-docs.md.
Document info
The two tables at the head of book.toml: what period the file covers, and whose business it is.
| Field | Type | XBRL GL 2015 element | Meaning | Required |
|---|---|---|---|---|
documentInfo | table | gl-cor:documentInfo | Metadata about this set of books | required |
documentInfo.entriesType | string, one of 12 | gl-cor:entriesType | What kind of data this book contains. Enumeration from GL 2015 specification. | required |
documentInfo.language | string | gl-cor:language | ISO 639-1 language code | optional |
documentInfo.creationDate | string | gl-cor:creationDate | When this book file was created (ISO 8601) | optional |
documentInfo.periodCoveredStart | string | gl-cor:periodCoveredStart | First day of the accounting period (ISO 8601) | required |
documentInfo.periodCoveredEnd | string | gl-cor:periodCoveredEnd | Last day of the accounting period (ISO 8601) | required |
documentInfo.defaultCurrency | string | gl-cor:defaultCurrency | ISO 4217 currency code | required |
documentInfo.entriesComment | string | gl-cor:entriesComment | Free text note about this book | optional |
documentInfo.diya-gl:vatStaggerGroup | integer, one of 3 | diya-gl extension | Which of HMRC's three VAT stagger groups this business's quarters fall on. Group 1 ends Mar/Jun/Sep/Dec, group 2 Apr/Jul/Oct/Jan, group 3 May/Aug/Nov/Feb. | optional |
documentInfo.diya-gl:payrollYearStart | string | diya-gl extension | The first day of the payroll calendar the Payslips workbook builds, when it differs from periodCoveredStart. | optional |
documentInfo.diya-gl:formatVersion | string | diya-gl extension | The diya-gl-books format name and version this file is written as, e.g. "diya-gl-books/1". | optional |
documentInfo.diya-gl:engineVersion | string | diya-gl extension | The npm package version and commit of the engine that wrote this file, e.g. "1.0.0+3f2a1b9c4d7e". | optional |
documentInfo.diya-gl:taxDataHash | string | diya-gl extension | A hash over every app/data/*.toml tax-year file, naming which rates this file's figures were calculated from. | optional |
documentInfo.diya-gl:templateHash | string | diya-gl extension | A hash over this product's own Excel template files, naming which workbook this file reproduces. | optional |
documentInfo.diya-gl:templateScorecard | string | diya-gl extension | The published reconciliation check counts for this product's template, e.g. "132 passed, 0 warnings, 0 failed". | optional |
documentInfo.diya-gl:reconciledCommit | string | diya-gl extension | The commit whose CI reconciliation run last passed for this product, the proof this file's figures rest on. | optional |
entityInformation | table | gl-cor:entityInformation + gl-bus:organizationInformation | Who this book belongs to | required |
entityInformation.organizationIdentifier | string | gl-bus:organizationIdentifier | Trading name or legal entity name | required |
entityInformation.organizationDescription | string | gl-bus:organizationDescription | Nature of the business | optional |
entityInformation.organizationAddressLine | string | gl-bus:organizationAddressLine | Registered or trading address, first line | optional |
entityInformation.organizationTown | string | gl-bus:organizationTown | Registered or trading address town/city | optional |
entityInformation.organizationPostcode | string | gl-bus:organizationPostcode | Registered or trading address postcode | optional |
entityInformation.organizationTelephone | string | gl-bus:organizationTelephone | Contact telephone number | optional |
entityInformation.taxRegistrationNumber | string | gl-taf:taxRegistrationNumber | HMRC Unique Taxpayer Reference (UTR) | optional |
entityInformation.taxAuthorityIdentifier | string | gl-taf:taxAuthorityIdentifier | Tax authority identifier | optional |
entityInformation.diya-gl:product | string, one of 7 | diya-gl extension | DIY Accounting product identifier. Determines which schema subset is active. | optional |
entityInformation.diya-gl:vatRegistered | boolean | diya-gl extension | Whether the business is VAT registered | optional |
entityInformation.diya-gl:basisOfAccounting | string, one of 2 | diya-gl extension | Accounting basis. Cash for sole traders, accrual for companies. | optional |
entityInformation.diya-gl:nino | string | diya-gl extension | National Insurance Number (format: AB123456C) | optional |
entityInformation.diya-gl:vatNumber | string | diya-gl extension | VAT registration number (9 digits) | optional |
entityInformation.diya-gl:companyNumber | string | diya-gl extension | Companies House number (8 digits, Company Accounts only) | optional |
entityInformation.diya-gl:cisRegistered | boolean | diya-gl extension | Whether the business is registered under the Construction Industry Scheme (CIS) | optional |
entityInformation.diya-gl:companiesHouseName | string | diya-gl extension | The first director's name as filed at Companies House, for the directors' report and CT600 (Company Accounts only). | optional |
entityInformation.diya-gl:mileageBasis | string, one of 2 | diya-gl extension | Whether the Taxi Driver return claims actual vehicle costs or the HMRC mileage allowance. Where absent, the calculator compares both and claims the higher, matching the shipped workbook's own comparison. | optional |
Accounts and registers
The chart of accounts, the registers a year-end needs (assets, hire purchase, members, charges) and the tax rates the period was calculated at. The rate tables hold HMRC's published figures for the year; the framework has no element for a rate table, so those rows name none.
| Field | Type | XBRL GL 2015 element | Meaning | Required |
|---|---|---|---|---|
directors | array of tables | diya-gl extension | Company directors and their shareholdings (Company Accounts only). Used to populate Companysecretary.xlsx sheets. | optional |
directors[].name | string | diya-gl extension | Director's full name | required |
directors[].role | string | diya-gl extension | Role title (e.g. Managing Director, Company Secretary, Non-Executive Director) | required |
directors[].shares | integer | diya-gl extension | Number of shares held | optional |
directors[].appointed | string | diya-gl extension | Date of appointment (ISO 8601) | optional |
directors[].resigned | string | diya-gl extension | Date of resignation if applicable (ISO 8601) | optional |
employees | array of tables | diya-gl extension | Employees on payroll. Used to populate Payslips.xlsx. Includes directors paid via PAYE. | optional |
employees[].employeeID | string | diya-gl extension | Unique employee identifier (matches diya-gl:employeeID in lines) | required |
employees[].name | string | diya-gl extension | Employee's full name | required |
employees[].role | string | diya-gl extension | Job title | optional |
employees[].grossPay | number | diya-gl extension | Gross pay per period (monthly or weekly amount) | required |
employees[].payFrequency | string, one of 4 | diya-gl extension | Pay frequency | required |
employees[].taxCode | string | diya-gl extension | HMRC tax code (e.g. 1257L) | required |
employees[].niCategory | string | diya-gl extension | NI category letter (A=standard, C=over state pension age, H=apprentice under 25, M=under 21) | optional |
employees[].startDate | string | diya-gl extension | Employment start date (ISO 8601) | optional |
employees[].isDirector | boolean | diya-gl extension | Whether this employee is also a company director | optional |
accounts | table | gl-cor:account | Chart of accounts. Maps to gl-cor:account tuples. Organised by journal for spreadsheet column mapping. | required |
accounts.sales | table | gl-cor:accountMainID | Sales/income account codes. Keys are gl-cor:accountMainID values. | required |
accounts.purchases | table | gl-cor:accountMainID | Purchase/expense account codes. Keys are gl-cor:accountMainID values. | required |
accounts.bank | table | gl-cor:accountMainID | Bank/cash account codes (Company Accounts). Keys are gl-cor:accountMainID values. | optional |
accounts.capital | table | gl-cor:accountMainID | Capital/equity account codes (Company Accounts). Keys are gl-cor:accountMainID values. | optional |
accounts.assets | table | gl-cor:accountMainID | Fixed/current asset account codes (Company Accounts). Keys are gl-cor:accountMainID values. | optional |
accounts.liabilities | table | gl-cor:accountMainID | Liability account codes (Company Accounts). Keys are gl-cor:accountMainID values. | optional |
openingBalances | table | diya-gl extension | The trial balance brought forward, reverse-engineered today from an opening journal by buildOpeningBalance(); this table lets a book state it directly. Every figure is positive; the balance sheet applies its own sign. | optional |
openingBalances.fixedAssetCost | table | diya-gl extension | An amount per fixed asset class. | optional |
openingBalances.fixedAssetCost.landBuildings | number | diya-gl extension | — | optional |
openingBalances.fixedAssetCost.plantMachinery | number | diya-gl extension | — | optional |
openingBalances.fixedAssetCost.fixturesFittings | number | diya-gl extension | — | optional |
openingBalances.fixedAssetCost.computerTechnology | number | diya-gl extension | — | optional |
openingBalances.fixedAssetCost.motorVehicles | number | diya-gl extension | — | optional |
openingBalances.fixedAssetDepreciation | table | diya-gl extension | An amount per fixed asset class. | optional |
openingBalances.fixedAssetDepreciation.landBuildings | number | diya-gl extension | — | optional |
openingBalances.fixedAssetDepreciation.plantMachinery | number | diya-gl extension | — | optional |
openingBalances.fixedAssetDepreciation.fixturesFittings | number | diya-gl extension | — | optional |
openingBalances.fixedAssetDepreciation.computerTechnology | number | diya-gl extension | — | optional |
openingBalances.fixedAssetDepreciation.motorVehicles | number | diya-gl extension | — | optional |
openingBalances.stock | number | diya-gl extension | — | optional |
openingBalances.tradeDebtors | number | diya-gl extension | — | optional |
openingBalances.tradeCreditors | number | diya-gl extension | — | optional |
openingBalances.longTermDebtors | number | diya-gl extension | — | optional |
openingBalances.bankAccounts | table | diya-gl extension | Opening balance per bank account, keyed by the account code declared in accounts.bank. | optional |
openingBalances.payeDue | number | diya-gl extension | — | optional |
openingBalances.vatDue | number | diya-gl extension | — | optional |
openingBalances.cisDue | number | diya-gl extension | — | optional |
openingBalances.netWagesDue | number | diya-gl extension | — | optional |
openingBalances.wageDeductionsDue | number | diya-gl extension | — | optional |
openingBalances.corporationTaxDue | number | diya-gl extension | — | optional |
openingBalances.dividendsDue | number | diya-gl extension | — | optional |
openingBalances.directorsLoan | number | diya-gl extension | — | optional |
openingBalances.longTermCreditors | number | diya-gl extension | — | optional |
openingBalances.shareCapital | number | diya-gl extension | — | optional |
openingBalances.retainedEarnings | number | diya-gl extension | — | optional |
openingBalances.capitalReserves | number | diya-gl extension | — | optional |
stock | table | diya-gl extension | Opening and closing stock, and the physical count taken at each. materialsPercent is the share of net sales that is direct materials; the Stock sheet's bought and sold columns stay at zero without it. | optional |
stock.openingValue | number | diya-gl extension | — | optional |
stock.closingValue | number | diya-gl extension | — | optional |
stock.openingCount | number | diya-gl extension | — | optional |
stock.closingCount | number | diya-gl extension | — | optional |
stock.materialsPercent | number | diya-gl extension | — | optional |
debtors | array of tables | diya-gl extension | Named trade debtors at the opening or closing balance sheet date. | optional |
debtors[].counterparty | string | diya-gl extension | — | required |
debtors[].invoice | string | diya-gl extension | — | optional |
debtors[].amount | number | diya-gl extension | — | required |
debtors[].timing | string, one of 2 | diya-gl extension | — | required |
creditors | array of tables | diya-gl extension | Named trade creditors at the opening or closing balance sheet date. | optional |
creditors[].counterparty | string | diya-gl extension | — | required |
creditors[].invoice | string | diya-gl extension | — | optional |
creditors[].amount | number | diya-gl extension | — | required |
creditors[].timing | string, one of 2 | diya-gl extension | — | required |
fixedAssets | array of tables | diya-gl extension | The fixed asset register: one entry per asset held at the opening balance sheet date or bought in the year. A capital allowance's balancing allowance on disposal reads taxWrittenDownValue, not accumulatedDepreciation, because the two schedules run at different rates. class is optional: the Basic Sole Trader and Taxi Driver Fixed Assets sheets carry a flat list with no asset-class blocks, unlike the Self Employed and Company Schedule. | optional |
fixedAssets[].assetID | string | diya-gl extension | Identifier matched by diya-gl:assetID on the lines that buy or dispose of this asset. | required |
fixedAssets[].class | string, one of 5 | diya-gl extension | The Schedule block this asset belongs to (Self Employed and Company only). | optional |
fixedAssets[].description | string | diya-gl extension | — | optional |
fixedAssets[].cost | number | diya-gl extension | — | required |
fixedAssets[].accumulatedDepreciation | number | diya-gl extension | — | optional |
fixedAssets[].taxWrittenDownValue | number | diya-gl extension | Written down value carried forward on the capital allowances computation, brought forward for an asset already held at the opening balance sheet date. | optional |
fixedAssets[].acquiredDate | string | diya-gl extension | — | optional |
fixedAssets[].depreciationRate | number | diya-gl extension | — | optional |
fixedAssets[].disposedDate | string | diya-gl extension | — | optional |
fixedAssets[].disposalProceeds | number | diya-gl extension | — | optional |
hpAgreements | array of tables | diya-gl extension | Hire purchase agreements financing an asset over a fixed term. The HPfinance sheet works out its own monthly payment, capital and interest split from these fields. | optional |
hpAgreements[].agreementID | string | diya-gl extension | Identifier matched by diya-gl:hpAgreement on the purchase line the agreement finances. | required |
hpAgreements[].description | string | diya-gl extension | — | optional |
hpAgreements[].financeCompany | string | diya-gl extension | — | optional |
hpAgreements[].supplier | string | diya-gl extension | — | optional |
hpAgreements[].amountFinanced | number | diya-gl extension | — | required |
hpAgreements[].adminCharges | number | diya-gl extension | — | required |
hpAgreements[].totalInterest | number | diya-gl extension | — | required |
hpAgreements[].termMonths | integer | diya-gl extension | — | required |
hpAgreements[].startDate | string | diya-gl extension | — | required |
dividends | array of tables | diya-gl extension | Dividends the board declared (Company Accounts only). The directors' report and the trial balance both read the declaration; the bank pays it in one or more instalments. | optional |
dividends[].declaredDate | string | diya-gl extension | — | optional |
dividends[].boardMeetingDate | string | diya-gl extension | — | required |
dividends[].amount | number | diya-gl extension | — | required |
members | array of tables | diya-gl extension | The register of members (Company Accounts only). A member is not always a director; directors[] holds the officers. | optional |
members[].memberID | string | diya-gl extension | Identifier matched by diya-gl:memberID on the dividend payment lines this member receives. | required |
members[].name | string | diya-gl extension | — | required |
members[].shares | integer | diya-gl extension | — | required |
members[].nominalValue | number | diya-gl extension | Nominal value per share. The shipped register currently prices every holding at a single nominal value set for the whole company, so a per-member figure here is not yet read. | optional |
members[].acquiredDate | string | diya-gl extension | — | optional |
charges | array of tables | diya-gl extension | Charges and debentures registered over the company's assets (Company Accounts only). Each secures a creditor falling due after more than one year. | optional |
charges[].chargeDate | string | diya-gl extension | — | optional |
charges[].description | string | diya-gl extension | The charged asset, as it reads on the register (e.g. 'Motor vehicles, being the company's delivery van'). | optional |
charges[].valuation | number | diya-gl extension | — | required |
charges[].holder | string | diya-gl extension | — | optional |
charges[].terms | string | diya-gl extension | — | optional |
charges[].boardMeetingDate | string | diya-gl extension | — | optional |
tax | table | — | Tax rates and thresholds. Values from HMRC published rates for the period. Used by validate and generate commands. Derived from gl-taf tax concepts. | optional |
tax.incomeTax | table | — | HMRC income tax rates and thresholds (England/Wales/NI) | optional |
tax.incomeTax.personalAllowance | number | — | — | optional |
tax.incomeTax.personalAllowanceTaperThreshold | number | — | — | optional |
tax.incomeTax.basicRate | number | — | — | optional |
tax.incomeTax.basicRateLimit | number | — | — | optional |
tax.incomeTax.higherRate | number | — | — | optional |
tax.incomeTax.higherRateThreshold | number | — | — | optional |
tax.incomeTax.additionalRate | number | — | — | optional |
tax.incomeTax.additionalRateThreshold | number | — | — | optional |
tax.nationalInsurance | table | — | HMRC National Insurance rates (self-employed Class 2 + 4, employed Class 1) | optional |
tax.nationalInsurance.class2WeeklyRate | number | — | — | optional |
tax.nationalInsurance.class2SmallProfitsThreshold | number | — | — | optional |
tax.nationalInsurance.class4MainRate | number | — | — | optional |
tax.nationalInsurance.class4UpperRate | number | — | — | optional |
tax.nationalInsurance.class4LowerProfits | number | — | — | optional |
tax.nationalInsurance.class4UpperProfits | number | — | — | optional |
tax.nationalInsurance.class1EmployeeMainRate | number | — | — | optional |
tax.nationalInsurance.class1EmployeeUpperRate | number | — | — | optional |
tax.nationalInsurance.class1EmployeePrimaryThreshold | number | — | — | optional |
tax.nationalInsurance.class1EmployeeUpperEarningsLimit | number | — | — | optional |
tax.nationalInsurance.class1EmployerRate | number | — | — | optional |
tax.nationalInsurance.class1EmployerSecondaryThreshold | number | — | — | optional |
tax.nationalInsurance.employmentAllowance | number | — | — | optional |
tax.vat | table | — | HMRC VAT rates and thresholds | optional |
tax.vat.standardRate | number | — | — | optional |
tax.vat.reducedRate | number | — | — | optional |
tax.vat.registrationThreshold | number | — | — | optional |
tax.vat.flatRateScheme | boolean | — | — | optional |
tax.vat.flatRatePercentage | number | — | — | optional |
tax.vat.staggerGroup | integer, one of 3 | — | Which of HMRC's three VAT stagger groups this business's quarters fall on. | optional |
tax.vat.firstPeriodEnd | string | — | The end date of the first VAT return period the book's data covers, so the return cycle is data rather than inferred from the sheet's own dates. | optional |
tax.corporationTax | table | — | HMRC Corporation Tax rates (Company Accounts only) | optional |
tax.corporationTax.smallProfitsRate | number | — | — | optional |
tax.corporationTax.smallProfitsLimit | number | — | — | optional |
tax.corporationTax.mainRate | number | — | — | optional |
tax.corporationTax.mainRateThreshold | number | — | — | optional |
tax.corporationTax.associatedCompanies | integer | — | — | optional |
tax.capitalAllowances | table | — | HMRC capital allowance rates | optional |
tax.capitalAllowances.annualInvestmentAllowance | number | — | — | optional |
tax.capitalAllowances.mainRateWDA | number | — | — | optional |
tax.capitalAllowances.specialRateWDA | number | — | — | optional |
tax.capitalAllowances.firstYearAllowanceRate | number | — | — | optional |
tax.mileage | table | — | HMRC approved mileage rates (Taxi Driver / Self Employed) | optional |
tax.mileage.carFirst10000 | number | — | Rate per mile, first 10,000 miles | optional |
tax.mileage.carOver10000 | number | — | Rate per mile, above 10,000 miles | optional |
tax.mileage.motorcycle | number | — | — | optional |
tax.mileage.bicycle | number | — | — | optional |
tax.dividends | table | — | Dividend tax rates (Company Accounts — director extraction) | optional |
tax.dividends.allowance | number | — | — | optional |
tax.dividends.basicRate | number | — | — | optional |
tax.dividends.higherRate | number | — | — | optional |
tax.dividends.additionalRate | number | — | — | optional |
One account
Each four-digit account code in a section holds one of these.
| Field | Type | XBRL GL 2015 element | Meaning | Required |
|---|---|---|---|---|
accountMainDescription | string | gl-cor:accountMainDescription | Human-readable account name | required |
accountType | string, one of 9 | gl-bus:accountType | GL account type enumeration | optional |
diya-gl:column | string | diya-gl extension | Excel column letter(s) for this account in the workbook | optional |
diya-gl:sa103sBox | string | diya-gl extension | SA103S box reference this account maps to (e.g. 'box10') | optional |
diya-gl:ct600Box | string | diya-gl extension | CT600 box reference (Company Accounts only) | optional |
diya-gl:vatBox | integer | diya-gl extension | VAT return box number (1-9) this account contributes to | optional |
Transaction lines
One JSON object per line of lines.jsonl. Four fields are required on every line, and a journal line must also say which side it posts.
| Field | Type | XBRL GL 2015 element | Meaning | Required |
|---|---|---|---|---|
entryNumber | string | gl-cor:entryNumber | Unique transaction identifier within this book | optional |
lineNumber | integer | gl-cor:lineNumber | Line within a multi-line journal entry (Company Accounts only) | optional |
sourceJournalID | string, one of 6 | gl-cor:sourceJournalID | Which journal/daybook this belongs to | required |
enteredDate | string | gl-cor:enteredDate | Date keyed (ISO 8601) | optional |
enteredBy | string | gl-cor:enteredBy | User or system that entered the transaction | optional |
postingDate | string | gl-cor:postingDate | Tax-relevant date (ISO 8601) | required |
accountMainID | string | gl-cor:accountMainID | Nominal code from the chart of accounts in book.toml. Must name an account the book declares; enforced by the validator, not this schema. | required |
accountMainDescription | string | gl-cor:accountMainDescription | Human-readable account name | optional |
accountSubID | string | gl-cor:accountSubID | Sub-account or department code | optional |
accountSubDescription | string | gl-cor:accountSubDescription | Sub-account description | optional |
debitCreditCode | string, one of 2 | gl-cor:debitCreditCode | D=Debit, C=Credit. Required on journal lines, because a journal line's sign is otherwise ambiguous. | optional |
amount | number | gl-cor:amount | Always positive, penny precision | required |
amountCurrency | string | gl-muc:amountCurrency | ISO 4217 currency code | optional |
documentType | string, one of 8 | gl-cor:documentType | Type of source document | optional |
documentReference | string | gl-cor:documentReference | Invoice/receipt number | optional |
documentNumber | string | gl-cor:documentNumber | Internal document number | optional |
documentDate | string | gl-cor:documentDate | Date on source document (ISO 8601) | optional |
detailComment | string | gl-cor:detailComment | Customer/supplier name or narrative | optional |
lineItemComment | string | gl-cor:detailComment | Description of goods/services | optional |
postingStatus | string, one of 3 | gl-cor:postingStatus | Entry status | optional |
taxCode | string, one of 7 | gl-taf | UK VAT code: S=Standard, R=Reduced, Z=Zero, E=Exempt, OS=Outside Scope, RC=Reverse Charge, NA=Not Applicable | optional |
taxAmount | number | gl-cor:taxAmount | VAT amount, always positive | optional |
taxRate | number | gl-taf:taxRate | VAT rate as decimal (0.20 = 20%) | optional |
taxPointDate | string | gl-taf:taxPointDate | VAT tax point if different from postingDate | optional |
taxDescription | string | gl-cor:taxDescription | Human-readable tax description | optional |
paymentMethod | string, one of 9 | gl-bus:paymentMethod | How payment was made/received | optional |
measurableQuantity | number | gl-bus:measurableQuantity | Non-monetary quantity (miles, hours, units) | optional |
measurableUnitOfMeasure | string, one of 6 | gl-bus:measurableUnitOfMeasure | Unit for measurableQuantity | optional |
measurableDescription | string | gl-bus:measurableQualifier | What is being measured | optional |
diya-gl:bankCode | string, one of 19 | diya-gl extension | Bank receipt/payment code, matching the analysis columns of the bank account workbooks. Transfers between accounts carry the letter of the account on the other side: BB=current, BS=savings, BC=cash, BD=credit card. Receipt only: K=interest received. Payment only: W=net wages, B=bank charges, J=interest paid, DV=dividend, RP=HMRC PAYE, RT=HMRC corporation tax. Either side: DR=trade debtor, CR=trade creditor, LDR=long term debtor, LCR=long term creditor, RV=HMRC VAT, RC=HMRC CIS, DL=directors loan, X=bank contra item. BC on a bank line with no other account on the other side is the workbook's own opening balance. | optional |
diya-gl:bankAccountID | string | diya-gl extension | Bank account code from book.toml accounts.bank (e.g. 1200=current, 1210=savings). Identifies which bank workbook this transaction belongs to. | optional |
diya-gl:employeeID | string | diya-gl extension | Employee identifier matching employees[] in book.toml. Used for payroll lines (sourceJournalID=payroll). | optional |
diya-gl:grossPay | number | diya-gl extension | Gross pay for this payroll period. Payroll lines only. | optional |
diya-gl:incomeTax | number | diya-gl extension | PAYE income tax deducted. Payroll lines only. | optional |
diya-gl:employeeNI | number | diya-gl extension | Employee National Insurance deducted. Payroll lines only. | optional |
diya-gl:employerNI | number | diya-gl extension | Employer National Insurance contribution. Payroll lines only. | optional |
diya-gl:netPay | number | diya-gl extension | Net pay (gross - tax - employee NI). Payroll lines only. | optional |
diya-gl:cisDeduction | number | diya-gl extension | CIS deduction amount withheld from sub-contractor payment. Purchases lines with CIS sub-contractors only. | optional |
diya-gl:hpAgreement | string | diya-gl extension | Reference of the hire purchase agreement that finances this purchase, matching an id on book.toml hpAgreements[]. A purchase carrying one reaches the books as an ordinary trade creditor; the year-end journal then moves the amount financed onto creditors falling due after more than one year. | optional |
diya-gl:cisRate | number | diya-gl extension | CIS deduction rate (0.20=20% standard, 0.30=30% higher, 0=gross payment). CIS lines only. | optional |
diya-gl:vatPeriodEnd | string | diya-gl extension | The VAT return period this line is declared on, when that period falls outside the accounting year the book covers. This is what makes a straddling entry representable: the line's postingDate sits in one accounting year, but its VAT is due on the return ending vatPeriodEnd. | optional |
diya-gl:assetID | string | diya-gl extension | The book.toml fixedAssets[] id this line moves: a capital purchase that registers the asset, or a disposal (an 'fs'-coded sale) that removes it. | optional |
diya-gl:memberID | string | diya-gl extension | The book.toml members[] id a dividend payment line settles. | optional |
Computed figures and their SA103S boxes
The three sole trader products fill the short self-employment pages, SA103S. The table gives every box on the 2026 form. figure means the product computes the box. derived means the page works it out from the profit and loss account. blank means the format carries nothing for that box, so it prints empty. Box numbers follow the 2026 form; nothing here is the HMRC document itself.
The last column is HMRC's own field name for the box in the Making Tax Digital Self Employment Business API, from HMRC's published SA103 mapping (sa103f_mapping_v3.csv, read 2026-09-04). 15 of the 34 boxes have one.
Self Employed books fill the full pages, SA103F, as well, and both they and Limited Company books fill a VAT return. Limited Company books file a CT600 and micro-entity accounts rather than SA103S. Every one of those views is on the product's own books page, and the figures behind them are on its reconciliation scorecard.
| Box | Label | Basic Sole Trader | Taxi Driver | Self Employed | MTD API field |
|---|---|---|---|---|---|
9 | Your turnover, the takings, fees, sales or money earned by your business | figure | figure | figure | periodIncome.turnover |
10 | Any other business income not included in box 9 | figure | figure | figure | periodIncome.other |
10.1 | Trading income allowance | blank | blank | blank | — |
11 | Costs of goods bought for resale or goods used | figure | derived | figure | periodExpenses.costOfGoods |
12 | Car, van and travel expenses, after private use proportion | figure | derived | figure | periodExpenses.carVanTravelExpenses |
13 | Wages, salaries and other staff costs | figure | derived | figure | periodExpenses.wagesAndStaffCosts |
14 | Rent, rates, power and insurance costs | figure | derived | figure | periodExpenses.premisesRunningCosts |
15 | Repairs and maintenance of property and equipment | figure | derived | figure | periodExpenses.maintenanceCosts |
16 | Accountancy, legal and other professional fees | derived | derived | figure | periodExpenses.professionalFees |
17 | Interest and bank and credit card financial charges | derived | derived | figure | periodExpenses.interestOnBankOtherLoans |
18 | Phone, fax, stationery and other office costs | derived | derived | figure | periodExpenses.adminCosts |
19 | Other allowable business expenses | derived | derived | figure | periodExpenses.otherExpenses |
20 | Total allowable expenses | derived | derived | figure | — |
21 | Net profit, if your business income is more than your expenses | figure | figure | figure | — |
22 | Or, net loss, if your expenses exceed your business income | figure | figure | figure | — |
23 | Annual Investment Allowance | figure | figure | figure | allowances.annualInvestmentAllowance |
24 | Allowance for small balance of unrelieved expenditure | figure | figure | figure | — |
24.1 | Zero-emission car allowance | blank | blank | blank | — |
25 | Other capital allowances | figure | figure | figure | — |
25.1 | The Structures and Buildings Allowance | blank | blank | blank | — |
25.2 | Freeport and Investment Zones Structures and Buildings Allowance | blank | blank | blank | — |
26 | Total balancing charges, for example where you have disposed of items for more than their tax value | figure | figure | figure | adjustments.balancingChargeOther | adjustments.balancingChargeBpra |
27 | Goods and/or services for your own use | figure | figure | figure | adjustments.goodsAndServicesOwnUse |
28 | Net business profit for tax purposes | figure | figure | figure | — |
29 | Loss brought forward from earlier years set off against this year's profits | figure | figure | figure | — |
30 | Any other business income not included in box 9 or box 10 | figure | figure | figure | adjustments.outstandingBusinessIncome |
31 | Total taxable profits from this business | figure | figure | figure | — |
32 | Net business loss for tax purposes | blank | blank | figure | — |
33 | Loss from this tax year set off against other income for 2025-26 | blank | blank | blank | — |
34 | Loss to be carried back to previous years and set off against income (or capital gains) | blank | blank | blank | — |
35 | Total loss to carry forward after all other set-offs, including unused losses brought forward | blank | blank | figure | — |
36 | Voluntary Class 2 NICs | blank | blank | blank | — |
37 | Exempt from Class 4 NICs | blank | blank | blank | — |
38 | Total CIS deductions taken from your payments by contractors | blank | blank | figure | — |
The check catalogue
A book is read in three stages. The two JSON Schemas validate book.toml and every line of lines.jsonl. The
validator then refuses a file whose lines reach an account the book does not declare, or name a hire purchase agreement, an asset or
a member the registers do not hold. Last, the 27 book checks below run over the data itself.
A check passes or fails, and a failure names the entries that caused it; 13 of the rules are checks. A
warning passes or warns and never blocks a save; 14 of them are warnings. Which rules a book runs
depends on the product it declares. Every result is written to bookchecks.json in the zip.
Beside these, the engine runs its reconciliation checks: one for every figure the workbook and the engine both compute, compared to the penny. Those depend on the books in hand rather than on the format, and every one of them is published on the scorecards below.
| Id | What it reads | Severity | Products |
|---|---|---|---|
book-accounts-in-chart | Every entry reaches an account the book's chart of accounts declares. | check | all |
book-amounts-whole-pence | Every amount is a whole number of pence. | check | all |
book-dates-in-period | Every entry is dated inside the accounting period the book declares. | check | all |
book-duplicate-entries | No two entries share the same journal, date, amount and detail without being each other's two sides. | warning | all |
book-empty-detail | Every entry names who or what it was with. | warning | all |
book-empty-month | Every month between the first and last entry has at least one entry. | warning | all |
book-negative-amount | Every sale and purchase amount is zero or more. | warning | all |
book-vat-threshold | Turnover for the year against the VAT registration threshold for that year. | warning | all |
book-bank-account-has-workbook | Every bank entry is on an account the package keeps a workbook for. | check | Self Employed |
book-bank-code-analysed | Every bank entry is coded to a column its workbook analyses. | check | Self Employed |
book-bank-line-has-side | Every bank entry says whether it is a receipt or a payment. | check | Self Employed |
book-fixed-asset-rows-fit | Every asset, disposal and hire purchase agreement has a row on the fixed asset schedule. | check | Self Employed |
book-ltd-bank-code-analysed | Every bank entry is coded to a column its workbook analyses. | check | Limited Company |
book-ltd-bank-line-has-side | Every bank entry says whether it is a receipt or a payment. | check | Limited Company |
book-ltd-fixed-asset-rows-fit-schedule | Every asset, disposal and hire purchase agreement has a row on the fixed asset schedule. | check | Limited Company |
book-ltd-payroll-line-names-employee | Every payroll entry names someone on the payroll. | check | Limited Company |
book-ltd-straddling-line-has-vat-period | Every sale and purchase dated outside the period names the VAT return period it belongs to. | check | Limited Company |
book-payslip-names-employee | Every payslip names someone the book employs. | check | Self Employed |
book-bank-overdrawn | The bank book closes every month at zero or more. | warning | Self Employed |
book-cash-never-overdrawn | The cash book closes every month at zero or more. | warning | Self Employed |
book-employee-paid-every-month | Every employee is paid in each month between their first and last payslip. | warning | Self Employed |
book-ltd-cis-on-subcontractor-line | Every CIS deduction sits on a sub-contractor purchase. | warning | Limited Company |
book-ltd-dividend-within-distributable-profits | Dividends declared against the retained profits available to pay them. | warning | Limited Company |
book-ltd-transfer-has-counter-leg | Every transfer between the company's own accounts appears on both of them. | warning | Limited Company |
book-taxi-fare-miles | Every fare day that carries miles elsewhere carries its own. | warning | Taxi Driver |
book-taxi-miles-band | Business miles stay inside the higher-rate mileage band. | warning | Taxi Driver |
book-taxi-vehicle-register | Every vehicle bought is on the fixed asset register. | warning | Taxi Driver |
The zip layout
The zip has no directories. Its entries are:
| Entry | What it holds |
|---|---|
book.toml | The business, the period, the chart of accounts and the registers. Always written. |
lines.jsonl | One transaction per line, in canonical order. Always written. |
report.json | Every figure the engine computed from the two files above, keyed by its cell reference. Always written. |
bookchecks.json | The result of every check and warning in the catalogue above, sorted by id. Written when the writer has run them. |
overtyped.json | Any cell a person typed over in the workbook, so the next generation keeps it. Written when there is one. |
Two writes of the same book produce the same bytes, on any machine and in any of the three surfaces (the browser page, the command line tool and the MCP server). The rules that make that hold:
- Lines sort by posting date, then source journal, account, entry number, line number, document reference and amount.
- Fields within a line follow the order the lines schema declares them in, and a field with no value is left out.
book.tomlfollows the book schema's table and key order, and every array of tables sorts by its own id field.- Money carries exactly two decimal places and a rate exactly four, trailing zeroes included.
- Dates are ISO 8601,
YYYY-MM-DD, with no time. - Every zip entry is stamped 1 January 1980, so the timestamp never varies between two writes.
The same book and lines also travel as one JSON file, with a
{ "format": "diya-gl-books", "version": 1, "product": ..., "book": ..., "lines": [...] }
envelope, and as that JSON zipped. The books pages read all three, plus a workbook and a package zip.
Versioning and provenance
The format is diya-gl-books version 1. The two schemas are at v2, and the version is
part of each schema's $id URL. A v1 existed but nothing outside this repository ever read it, so v2 replaced it in place.
Five stamps sit in the book's document info and at the head of report.json. They answer the question a file raises
years later. Recalculate a 2026 book in 2030 and it either reproduces its report.json byte for byte, or names the
stamp that differs.
| Stamp | Value | What it answers | Status |
|---|---|---|---|
| format version | diya-gl-books 1 | Can this tool read this file | written today |
| engine version | the package version, and the commit it was built from | Which code produced these figures | written today |
| tax-data version | a hash over the year's rate files | Which rates were applied | written today |
| template version | a hash over the product's workbook templates | Which workbook this reproduces | written today |
| reconciled commit | the commit whose reconciliation run passed | The proof this release rests on | written by the reconciliation workflows' commit job, empty until the first one runs |
Reconciliation evidence
Every figure this format computes is checked against the same figure in the spreadsheet. For each product, CI generates the package from a set of books, recalculates the workbook in LibreOffice, computes the same figures from the diya-gl data alone, and compares the two to the penny. Each scorecard carries the whole run: its status, the checks, the input transactions, screenshots of the recalculated sheets, the accounting statements and the tax review.
| Product | Featured scenario |
|---|---|
| Basic Sole Trader | bst-scenario-basic |
| Taxi Driver | taxi-scenario-basic |
| Self Employed | se-scenario-advanced |
| Limited Company | ltd-scenario-full |
Getting the tools
The books pages read and write the format in your browser. Nothing you load leaves the machine.
The same engine runs on the command line and as an MCP server, published as the npm package
@diy-accounting-uk/diya-gl. It is not on npm yet; this page will carry the install line when it is.
The spreadsheets themselves stay free to download on a donation basis. Download a package, or chip in if the tools earn it.