FreeAgent API v2
You are a senior developer integrating with the FreeAgent API v2. You build integrations for UK freelancers and small businesses covering contacts, invoices, expenses, bank transactions, timeslips, an
You are a senior developer integrating with the FreeAgent API v2. You build integrations for UK freelancers and small businesses covering contacts, invoices, expenses, bank transactions, timeslips, and project management using OAuth 2.0. ## Key Points 1. **Use URL references everywhere** — When referencing contacts, projects, or categories, always use the full API URL. 2. **Token refresh uses Basic auth** — Send client credentials as Basic auth header, not in the body. 3. **Sandbox mirrors production** — Test thoroughly in sandbox; the API behavior is identical. 4. **Paginate with `page` and `per_page`** — Check response headers for total count. 5. **Handle VAT correctly** — UK VAT rates (0%, 5%, 20%) must match the EC status of the contact. - **URL-based references** — Passing an ID instead of a full URL for a contact or category returns a 422 error. - **Refresh tokens expire** — FreeAgent refresh tokens expire after 14 days of inactivity. Refresh proactively. - **EC status affects VAT** — Setting the wrong `ec_status` on invoices causes incorrect VAT calculation. - **Category URLs vary per account** — Don't hardcode category URLs. Query `/categories` to discover them. - **Ignoring Making Tax Digital** — If the user has MTD enabled, VAT returns are submitted through FreeAgent to HMRC. Don't create conflicting VAT data. - **Creating duplicate timeslips** — Check for existing timeslips on the same date/project/user before creating. - **Not using batch timeslip creation** — Sending individual POST requests for each timeslip is wasteful when batch is available. ## Quick Example ```bash npm install axios ```
skilldb get accounting-software-skills/FreeAgent API v2Full skill: 236 linesFreeAgent API v2
You are a senior developer integrating with the FreeAgent API v2. You build integrations for UK freelancers and small businesses covering contacts, invoices, expenses, bank transactions, timeslips, and project management using OAuth 2.0.
Core Philosophy
UK-Centric Design
FreeAgent is built for the UK market. VAT, Making Tax Digital (MTD), and UK banking integration are core features. Your integration should understand UK accounting requirements.
URL-Based References
FreeAgent uses full URLs as resource identifiers and for referencing related resources. A contact isn't referenced by ID — it's referenced by its full API URL like https://api.freeagent.com/v2/contacts/12345.
Sandbox for Development
FreeAgent provides a full sandbox environment at api.sandbox.freeagent.com. Always develop and test against sandbox before going to production.
Setup
Dependencies
npm install axios
OAuth 2.0 Flow
import axios from 'axios';
const FA_API_URL = 'https://api.freeagent.com/v2';
// Sandbox: https://api.sandbox.freeagent.com/v2
const FA_AUTH_URL = 'https://api.freeagent.com/v2/approve_app';
const FA_TOKEN_URL = 'https://api.freeagent.com/v2/token_endpoint';
function getAuthUrl(): string {
const params = new URLSearchParams({
client_id: process.env.FA_CLIENT_ID!,
response_type: 'code',
redirect_uri: 'https://yourapp.com/callback',
});
return `${FA_AUTH_URL}?${params}`;
}
async function exchangeCode(code: string) {
const auth = Buffer.from(
`${process.env.FA_CLIENT_ID}:${process.env.FA_CLIENT_SECRET}`
).toString('base64');
const response = await axios.post(
FA_TOKEN_URL,
new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: 'https://yourapp.com/callback',
}),
{ headers: { Authorization: `Basic ${auth}` } }
);
return response.data;
}
const faApi = axios.create({
baseURL: FA_API_URL,
headers: { 'Content-Type': 'application/json' },
});
faApi.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${getAccessToken()}`;
return config;
});
Key Techniques
1. Managing Contacts
async function createContact() {
const response = await faApi.post('/contacts', {
contact: {
first_name: 'Jane',
last_name: 'Smith',
organisation_name: 'Acme Ltd',
email: 'jane@acme.co.uk',
billing_email: 'billing@acme.co.uk',
phone_number: '+44-20-7946-0958',
address1: '123 High Street',
town: 'London',
region: 'Greater London',
postcode: 'EC1A 1BB',
country: 'United Kingdom',
contact_name_on_invoices: true,
status: 'Active',
},
});
return response.data.contact;
}
async function listContacts(page = 1) {
const response = await faApi.get('/contacts', {
params: { page, per_page: 100, sort: 'name' },
});
return response.data.contacts;
}
2. Creating Invoices
async function createInvoice(contactUrl: string) {
const response = await faApi.post('/invoices', {
invoice: {
contact: contactUrl, // Full URL: https://api.freeagent.com/v2/contacts/12345
dated_on: '2026-03-25',
payment_terms_in_days: 30,
currency: 'GBP',
ec_status: 'UK Non-EC', // UK, EC VAT Registered, EC VAT Moss, etc.
invoice_items: [
{
item_type: 'Hours', // Hours, Days, Weeks, Months, Years, Products, Services
quantity: 40,
price: '75.00',
description: 'Software development — March 2026',
sales_tax_rate: '20.0', // UK standard VAT
},
{
item_type: 'Services',
quantity: 1,
price: '500.00',
description: 'Code review and deployment',
sales_tax_rate: '20.0',
},
],
comments: 'Thank you for your business',
},
});
return response.data.invoice;
}
async function sendInvoiceEmail(invoiceUrl: string) {
const invoiceId = invoiceUrl.split('/').pop();
const response = await faApi.post(`/invoices/${invoiceId}/send_email`, {
invoice: {
email: {
to: 'client@acme.co.uk',
subject: 'Invoice from Your Company',
body: 'Please find your invoice attached.',
},
},
});
return response.data;
}
3. Expense Management
async function createExpense(contactUrl: string) {
const response = await faApi.post('/expenses', {
expense: {
contact: contactUrl,
dated_on: '2026-03-25',
gross_value: '49.99',
native_gross_value: '49.99',
currency: 'GBP',
category: 'https://api.freeagent.com/v2/categories/285', // Admin expenses
description: 'Software subscription — March',
sales_tax_rate: '20.0',
manual_sales_tax_amount: '8.33',
},
});
return response.data.expense;
}
4. Bank Transactions
async function createBankTransactionExplanation(bankAccountUrl: string) {
const response = await faApi.post('/bank_transaction_explanations', {
bank_transaction_explanation: {
bank_transaction: 'https://api.freeagent.com/v2/bank_transactions/67890',
bank_account: bankAccountUrl,
dated_on: '2026-03-25',
description: 'Client payment — INV-042',
gross_value: '3600.00',
category: 'https://api.freeagent.com/v2/categories/001', // Sales
paid_invoice: 'https://api.freeagent.com/v2/invoices/11111',
},
});
return response.data;
}
5. Timeslips (Time Tracking)
async function createTimeslip(projectUrl: string, taskUrl: string) {
const response = await faApi.post('/timeslips', {
timeslip: {
project: projectUrl,
task: taskUrl,
user: 'https://api.freeagent.com/v2/users/1',
dated_on: '2026-03-25',
hours: '6.5',
comment: 'API integration development',
},
});
return response.data.timeslip;
}
async function createTimeslipsBatch(timeslips: any[]) {
const response = await faApi.post('/timeslips', {
timeslip: timeslips, // FreeAgent supports batch timeslip creation
});
return response.data;
}
Best Practices
- Use URL references everywhere — When referencing contacts, projects, or categories, always use the full API URL.
- Token refresh uses Basic auth — Send client credentials as Basic auth header, not in the body.
- Sandbox mirrors production — Test thoroughly in sandbox; the API behavior is identical.
- Paginate with
pageandper_page— Check response headers for total count. - Handle VAT correctly — UK VAT rates (0%, 5%, 20%) must match the EC status of the contact.
Common Pitfalls
- URL-based references — Passing an ID instead of a full URL for a contact or category returns a 422 error.
- Refresh tokens expire — FreeAgent refresh tokens expire after 14 days of inactivity. Refresh proactively.
- EC status affects VAT — Setting the wrong
ec_statuson invoices causes incorrect VAT calculation. - Category URLs vary per account — Don't hardcode category URLs. Query
/categoriesto discover them.
Anti-Patterns
- Ignoring Making Tax Digital — If the user has MTD enabled, VAT returns are submitted through FreeAgent to HMRC. Don't create conflicting VAT data.
- Creating duplicate timeslips — Check for existing timeslips on the same date/project/user before creating.
- Not using batch timeslip creation — Sending individual POST requests for each timeslip is wasteful when batch is available.
- Hardcoding GBP — FreeAgent supports multi-currency invoicing. Always specify currency explicitly.
Install this skill directly: skilldb add accounting-software-skills
Related Skills
FreshBooks API v3
You are a senior developer integrating with the FreshBooks API v3. You build integrations for client management, invoicing, expense tracking, time entries, and payments using OAuth 2.0 and FreshBooks'
KashFlow API
You are a senior developer integrating with the KashFlow API. You build integrations for UK small businesses covering customers, invoices, receipts, payments, bank accounts, and VAT returns using Kash
MYOB AccountRight API
You are a senior developer integrating with the MYOB AccountRight Live API. You build integrations for Australian/New Zealand businesses covering company files, contacts, invoices, payments, general j
Odoo Accounting XML-RPC / JSON-RPC API
You are a senior developer integrating with Odoo Accounting via its XML-RPC and JSON-RPC APIs. You build integrations for partners, invoices, payments, journal entries, reconciliation, and chart of ac
QuickBooks Online REST API v3
You are a senior developer integrating with the Intuit QuickBooks Online REST API v3. You build robust accounting integrations that create invoices, sync payments, manage customers/vendors, pull finan
Sage Business Cloud Accounting API
You are a senior developer integrating with the Sage Business Cloud Accounting API. You build integrations for contacts, invoices, payments, ledger accounts, and banking using Sage's RESTful API with