A typed TypeScript SDK for the Monzo developer API.
Built against the live API, not just the docs. The official documentation is incomplete in a few areas and this library attempts to fill those gaps (see Notes on the docs).
npm install github:stephendotgg/monzo-api-tsimport { MonzoClient } from 'monzo-api-ts'
const client = new MonzoClient({ accessToken: 'your_access_token' })
// Verify authentication
const identity = await client.auth.whoami()
// List accounts
const accounts = await client.accounts.list()
const [account] = accounts
// Read balance (amounts are in minor units - pence for GBP)
const balance = await client.balance.read(account.id)
console.log(balance.balance) // e.g. 100050 (= £1000.50)
// List recent transactions
const transactions = await client.transactions.list({ account_id: account.id, limit: 20 })
// Auto-paginate through all transactions
for await (const tx of client.transactions.listAll({ account_id: account.id })) {
console.log(tx.description, tx.amount)
}
// Pots
const pots = await client.pots.list(account.id)
await client.pots.deposit(pots[0].id, {
source_account_id: account.id,
amount: 500,
dedupe_id: crypto.randomUUID(),
})Token exchange and refresh are standalone functions rather than methods on MonzoClient since they run before you have an access token:
import { exchangeToken, refreshToken } from 'monzo-api-ts'
const tokens = await exchangeToken({
client_id: '...',
client_secret: '...',
redirect_uri: 'https://yourapp.com/callback',
code: 'auth_code_from_redirect',
})
const client = new MonzoClient({ accessToken: tokens.access_token })
// Refresh when the token expires
const refreshed = await refreshToken({
client_id: '...',
client_secret: '...',
refresh_token: tokens.refresh_token,
})
// Invalidate the token immediately
await client.auth.logout()const client = new MonzoClient({
accessToken: 'your_access_token',
timeout: 10_000, // request timeout in ms (default: 30000)
maxRetries: 3, // retries on 429 responses (default: 2)
})Retries use the Retry-After header value from the 429 response to determine the wait before each attempt.
import type { WebhookEvent } from 'monzo-api-ts'
const webhook = await client.webhooks.create(account.id, 'https://yourapp.com/webhook')
function handleWebhook(event: WebhookEvent) {
if (event.type === 'transaction.created') {
console.log(event.data.description, event.data.amount)
}
}All monetary values in the Monzo API are integers in minor units (pence for GBP, cents for EUR/USD). Two helpers are exported to avoid converting this everywhere:
import { toMajorUnits, formatAmount } from 'monzo-api-ts'
toMajorUnits(1050) // 10.5
formatAmount(1050, 'GBP') // '£10.50'
formatAmount(-350, 'GBP') // '-£3.50'| Resource | Methods |
|---|---|
auth |
whoami(), logout() |
accounts |
list() |
balance |
read(accountId) |
transactions |
list(), get(), annotate(), listAll() |
pots |
list(), deposit(), withdraw() |
webhooks |
list(), create(), delete() |
feed |
create() |
attachments |
upload(), register(), deregister() |
receipts |
save(), get(), delete() |
The official Monzo developer documentation is fairly sparse and has not kept up with the API in a few places. These are the gaps found while building and testing this SDK against a real account:
Account types
The docs list three account types: uk_retail, uk_retail_joint, and uk_monzo_flex. The live API also returns uk_rewards (Monzo Rewards) and uk_business (Monzo Business), neither of which are documented. This SDK accepts any string for the type field and exposes the known values as a TypeScript union.
Transaction fields
The docs imply all transaction fields are always present. In practice, several are absent depending on the account type and transaction state:
account_balanceis not returned for certain account types (Flex, Business)settled,local_amount, andlocal_currencyare absent on pending transactionsis_load,updated, andmetadataare occasionally missing
All of these are typed as optional in this library.
Transaction access window
The docs mention that after 5 minutes of authentication, only the last 90 days of transactions are accessible. In practice this is enforced, but the error message from the API is a generic 403 rather than anything descriptive.
Pagination
The docs describe since/before pagination for transactions but give no indication of what the default or maximum limit value is. Testing shows the default is 30 and the maximum is 100 per request. The listAll() method on the transactions resource handles pagination automatically.
MIT