freeagent

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package freeagent is a client for the FreeAgent v2 API.

See https://dev.freeagent.com for the upstream reference. Resource types mirror the documented payloads; cross-references are carried as ResourceURL, and monetary values as Decimal, never float64.

Example (NewClient)

Build a client backed by a file-stored token that refreshes itself. This is the shape the README documents; keeping it here means it cannot drift.

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/alekc/freeagent-sdk/freeagent"
)

func main() {
	ctx := context.Background()

	path, err := freeagent.DefaultTokenPath()
	if err != nil {
		log.Fatal(err)
	}
	store, err := freeagent.NewFileStore(path, freeagent.Sandbox.Name)
	if err != nil {
		log.Fatal(err)
	}
	config := freeagent.Sandbox.OAuthConfig(
		os.Getenv("FREEAGENT_CLIENT_ID"),
		os.Getenv("FREEAGENT_CLIENT_SECRET"),
		"http://localhost:8723/callback",
	)
	source, err := freeagent.NewTokenSource(ctx, config, store)
	if err != nil {
		log.Fatal(err)
	}

	client, err := freeagent.NewClient(
		freeagent.WithBaseURL(freeagent.Sandbox.BaseURL),
		freeagent.WithTokenSource(source),
		freeagent.WithUserAgent("my-app/1.0"),
	)
	if err != nil {
		log.Fatal(err)
	}

	body, _, err := client.Raw(ctx, http.MethodGet, "company", nil, nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(body))
}

Index

Examples

Constants

View Source
const (
	BankAccountTypeStandard        = "StandardBankAccount"
	BankAccountTypeCreditCard      = "CreditCardAccount"
	BankAccountTypeEcommerce       = "EcommerceAccount"
	BankAccountTypeRentalStatement = "RentalStatementAccount"
	BankAccountTypeAmazonSeller    = "AmazonSellerBankAccount"
	BankAccountTypeGoCardless      = "GocardlessBankAccount"
	BankAccountTypePaypalClassic   = "PaypalClassicAccount"
	BankAccountTypePaypalCurrency  = "Paypal::CurrencyAccount"
	BankAccountTypeStripe          = "StripeBankAccount"
)

Bank account type values accepted by BankAccount.Type.

View Source
const (
	BankAccountViewStandard = "standard_bank_accounts"
	//nolint:gosec // G101 false positive: this is a query filter, not a credential
	BankAccountViewCreditCard = "credit_card_accounts"
	BankAccountViewPaypal     = "paypal_accounts"
)

Views accepted by the bank accounts list endpoint.

View Source
const (
	BankFeedTypeAPI         = "api"
	BankFeedTypeOpenBanking = "open_banking"
)

Feed types returned by BankFeed.FeedType.

View Source
const (
	CISBandGross    = "cis_gross"
	CISBandStandard = "cis_standard"
	CISBandHigher   = "cis_higher"
)

CIS band names.

View Source
const (
	BillViewAll                   = "all"
	BillViewOpen                  = "open"
	BillViewOverdue               = "overdue"
	BillViewOpenOrOverdue         = "open_or_overdue"
	BillViewOpenOrOverduePayments = "open_or_overdue_payments"
	BillViewOpenOrOverdueRefunds  = "open_or_overdue_refunds"
	BillViewPaid                  = "paid"
	BillViewRecurring             = "recurring"
	BillViewHirePurchase          = "hire_purchase"
	BillViewCIS                   = "cis"
)

Views accepted by the bills list endpoint.

View Source
const (
	DepreciationStraightLine     = "straight_line"
	DepreciationReducingBalance  = "reducing_balance"
	DepreciationNone             = "no_depreciation"
	DepreciationFrequencyMonthly = "monthly"
	DepreciationFrequencyAnnual  = "annually"
)

Depreciation methods accepted by DepreciationProfile.

View Source
const (
	CapitalAssetViewAll        = "all"
	CapitalAssetViewDisposed   = "disposed"
	CapitalAssetViewDisposable = "disposable"
)

Views accepted by the capital assets list endpoint.

View Source
const (
	CategoryGroupAdminExpenses = "admin_expenses_categories"
	CategoryGroupCostOfSales   = "cost_of_sales_categories"
	CategoryGroupIncome        = "income_categories"
	CategoryGroupGeneral       = "general_categories"
)

The four envelope keys the categories endpoint groups results under.

View Source
const (
	DefaultBaseURL = "https://api.freeagent.com/v2/"
	SandboxBaseURL = "https://api.sandbox.freeagent.com/v2/"
)

Endpoints. Both carry the /v2/ prefix and a trailing slash so relative resource paths resolve against them.

View Source
const (
	ContactViewAll               = "all"
	ContactViewActive            = "active"
	ContactViewClients           = "clients"
	ContactViewSuppliers         = "suppliers"
	ContactViewActiveProjects    = "active_projects"
	ContactViewCompletedProjects = "completed_projects"
	ContactViewOpenClients       = "open_clients"
	ContactViewOpenSuppliers     = "open_suppliers"
	ContactViewHidden            = "hidden"
)

Views accepted by the contacts list endpoint.

View Source
const (
	CreditNoteViewAll = "all"
	//nolint:gosec // G101 false positive: this is a query filter, not a credential
	CreditNoteViewRecentOpenOverdue = "recent_open_or_overdue"
	CreditNoteViewOpen              = "open"
	CreditNoteViewOverdue           = "overdue"
	CreditNoteViewOpenOrOverdue     = "open_or_overdue"
	CreditNoteViewDraft             = "draft"
	CreditNoteViewRefunded          = "refunded"
)

Views accepted by the credit notes list endpoint.

View Source
const (
	EstimateViewAll      = "all"
	EstimateViewRecent   = "recent"
	EstimateViewDraft    = "draft"
	EstimateViewNonDraft = "non_draft"
	EstimateViewSent     = "sent"
	EstimateViewApproved = "approved"
	EstimateViewRejected = "rejected"
	EstimateViewInvoiced = "invoiced"
)

Views accepted by the estimates list endpoint.

View Source
const (
	ExpenseViewRecent    = "recent"
	ExpenseViewRecurring = "recurring"
)

Views accepted by the expenses list endpoint.

View Source
const (
	FilingStatusDraft         = "draft"
	FilingStatusUnfiled       = "unfiled"
	FilingStatusPending       = "pending"
	FilingStatusRejected      = "rejected"
	FilingStatusFiled         = "filed"
	FilingStatusMarkedAsFiled = "marked_as_filed"
)

Filing statuses a FinalAccountsReport can hold.

View Source
const (
	InvoiceViewAll               = "all"
	InvoiceViewRecentOpenOverdue = "recent_open_or_overdue"
	InvoiceViewOpen              = "open"
	InvoiceViewOverdue           = "overdue"
	InvoiceViewOpenOrOverdue     = "open_or_overdue"
	InvoiceViewDraft             = "draft"
	InvoiceViewPaid              = "paid"
	InvoiceViewScheduledToEmail  = "scheduled_to_email"
	InvoiceViewThankYouEmails    = "thank_you_emails"
	InvoiceViewReminderEmails    = "reminder_emails"
)

Views accepted by the invoices list endpoint. LastNMonths is a template: substitute the month count, for example "last_3_months".

View Source
const (
	PaymentStatusUnpaid       = "unpaid"
	PaymentStatusMarkedAsPaid = "marked_as_paid"
)

Payment statuses used across the filing families.

View Source
const (
	ProjectViewActive    = "active"
	ProjectViewCompleted = "completed"
	ProjectViewCancelled = "cancelled"
	ProjectViewHidden    = "hidden"
)

Views accepted by the projects list endpoint.

View Source
const (
	DefaultRequestsPerMinute = 100
	DefaultRequestsPerHour   = 3400
)

FreeAgent enforces 120 requests per minute and 3600 per hour per end user. The defaults below sit under both: the token buckets refill continuously while the API counters reset on fixed boundaries, so burst plus refill must still fit inside a single window.

View Source
const (
	VATStatusOutOfScope = "out_of_scope"
	VATStatusReduced    = "reduced"
	VATStatusStandard   = "standard"
	VATStatusZero       = "zero"
)

VAT statuses accepted by PriceListItem.VATStatus.

View Source
const (
	TaskViewAll       = "all"
	TaskViewActive    = "active"
	TaskViewCompleted = "completed"
	TaskViewHidden    = "hidden"
)

Views accepted by the tasks list endpoint.

View Source
const (
	TimeslipViewAll      = "all"
	TimeslipViewUnbilled = "unbilled"
	TimeslipViewRunning  = "running"
)

Views accepted by the timeslips list endpoint.

View Source
const (
	RecurringInvoiceViewDraft    = "draft"
	RecurringInvoiceViewActive   = "active"
	RecurringInvoiceViewInactive = "inactive"
)

Views accepted by the recurring invoices list endpoint.

View Source
const (
	DateLayout      = "2006-01-02"
	TimestampLayout = "2006-01-02T15:04:05.000Z07:00"
)

Layouts used on the wire. FreeAgent documents dates as YYYY-MM-DD and timestamps as ISO 8601 with milliseconds, which is what updated_since filters expect to receive back.

View Source
const (
	UserViewAll            = "all"
	UserViewStaff          = "staff"
	UserViewActiveStaff    = "active_staff"
	UserViewAdvisors       = "advisors"
	UserViewActiveAdvisors = "active_advisors"
)

Views accepted by the users list endpoint.

View Source
const DefaultAPIVersion = "2026-08-16"

DefaultAPIVersion pins the X-Api-Version header. Sending no header opts into pre-versioning behaviour, which drifts from the documentation this library was modelled on, so a date is always sent. Bump it only alongside the model changes the new version implies.

View Source
const DefaultMaxResponseBytes = 32 << 20

DefaultMaxResponseBytes bounds a single response body. Legitimate pages of 100 records are far smaller; the cap exists so a broken or hostile upstream cannot exhaust memory.

View Source
const MaxAttachmentBytes = 5 << 20

MaxAttachmentBytes is the upload limit FreeAgent documents for the attachment field on bills, expenses and bank transaction explanations.

View Source
const MaxPerPage = 100

MaxPerPage is the largest page size FreeAgent accepts.

View Source
const Version = "0.1.0-dev"

Version is the library version reported in the default user agent.

Variables

View Source
var (
	Production = Environment{
		Name:         "production",
		BaseURL:      DefaultBaseURL,
		AuthorizeURL: "https://api.freeagent.com/v2/approve_app",
		TokenURL:     "https://api.freeagent.com/v2/token_endpoint",
	}
	Sandbox = Environment{
		Name:         "sandbox",
		BaseURL:      SandboxBaseURL,
		AuthorizeURL: "https://api.sandbox.freeagent.com/v2/approve_app",
		TokenURL:     "https://api.sandbox.freeagent.com/v2/token_endpoint",
	}
)

The two deployments FreeAgent operates.

View Source
var (
	ErrUnauthorized = errors.New("freeagent: unauthorized")
	ErrForbidden    = errors.New("freeagent: forbidden")
	ErrNotFound     = errors.New("freeagent: not found")
	ErrValidation   = errors.New("freeagent: validation failed")
	ErrRateLimited  = errors.New("freeagent: rate limited")
	ErrServer       = errors.New("freeagent: server error")
)

Sentinels for errors.Is. They classify by HTTP status so callers can branch without importing net/http or matching on message text.

View Source
var DefaultRetryPolicy = RetryPolicy{
	MaxAttempts:   3,
	BaseDelay:     500 * time.Millisecond,
	MaxDelay:      30 * time.Second,
	MaxRetryAfter: 2 * time.Minute,
}

DefaultRetryPolicy is applied unless overridden.

View Source
var DefaultUserAgent = "freeagent-sdk-go/" + Version + " (+https://github.com/alekc/freeagent-sdk)"

DefaultUserAgent identifies the library. FreeAgent asks integrations to send something identifying, and callers should append their own name via WithUserAgent.

View Source
var ErrNoToken = errors.New("freeagent: no stored token")

ErrNoToken is returned by a TokenStore that holds no token yet. It is the signal for tooling to run the authorisation flow.

View Source
var ErrNoTokenSource = errors.New("freeagent: no token source configured, pass WithTokenSource or WithoutAuth")

ErrNoTokenSource is returned by NewClient when neither WithTokenSource nor WithoutAuth was supplied. Every FreeAgent endpoint requires a bearer token, so an unauthenticated client is a configuration mistake worth catching at construction rather than on the first 401.

View Source
var ErrNotAMember = errors.New("freeagent: resource URL does not address a collection member")

ErrNotAMember is returned by ResourceURL.ID for URLs that do not address a single member of a collection, such as the /v2/company singleton.

View Source
var ErrReadOnly = errors.New("freeagent: client is read-only")

ErrReadOnly is returned instead of sending a mutating request on a client built WithReadOnly.

View Source
var Resources = map[string]ResourceMeta{

	"attachments": {
		Name: "attachments", Path: "attachments",
		Singular: "attachment", NoList: true,
		Doc: "https://dev.freeagent.com/docs/attachments",
	},
	"balance_sheet": {
		Name: "balance_sheet", Path: "accounting/balance_sheet",
		Singleton: true, ReadOnly: true,
		Doc: "https://dev.freeagent.com/docs/balance_sheet",
	},
	"bank_accounts": {
		Name: "bank_accounts", Path: "bank_accounts",
		Singular: "bank_account", Plural: "bank_accounts",
		Doc: "https://dev.freeagent.com/docs/bank_accounts",
	},
	"bank_transaction_explanations": {
		Name: "bank_transaction_explanations", Path: "bank_transaction_explanations",
		Singular: "bank_transaction_explanation", Plural: "bank_transaction_explanations",
		RequiresBankAccount: true,
		Doc:                 "https://dev.freeagent.com/docs/bank_transaction_explanations",
	},

	"bank_transactions": {
		Name: "bank_transactions", Path: "bank_transactions",
		Singular: "bank_transaction", Plural: "bank_transactions",
		ReadOnly: true, RequiresBankAccount: true,
		Doc: "https://dev.freeagent.com/docs/bank_transactions",
	},

	"bank_feeds": {
		Name: "bank_feeds", Path: "bank_feeds",
		Singular: "bank_feed", Plural: "bank_feeds",
		ReadOnly: true,
		Doc:      "https://dev.freeagent.com/docs/bank_feeds",
	},
	"bills": {
		Name: "bills", Path: "bills",
		Singular: "bill", Plural: "bills",
		Doc: "https://dev.freeagent.com/docs/bills",
	},

	"categories": {
		Name: "categories", Path: "categories",
		Singular: "category", Grouped: true,
		Doc: "https://dev.freeagent.com/docs/categories",
	},
	"capital_asset_types": {
		Name: "capital_asset_types", Path: "capital_asset_types",
		Singular: "capital_asset_type", Plural: "capital_asset_types",
		Doc: "https://dev.freeagent.com/docs/capital_asset_types",
	},

	"capital_assets": {
		Name: "capital_assets", Path: "capital_assets",
		Singular: "capital_asset", Plural: "capital_assets",
		ReadOnly: true,
		Doc:      "https://dev.freeagent.com/docs/capital_assets",
	},

	"cashflow": {
		Name: "cashflow", Path: "cashflow",
		Singular: "cashflow", Singleton: true, ReadOnly: true,
		Doc: "https://dev.freeagent.com/docs/cashflow",
	},

	"cis_bands": {
		Name: "cis_bands", Path: "cis_bands",
		Singleton: true, ReadOnly: true, CustomEnvelope: true,
		Doc: "https://dev.freeagent.com/docs/cis_bands",
	},
	"company": {
		Name: "company", Path: "company",
		Singular: "company", Singleton: true, ReadOnly: true,
		Doc: "https://dev.freeagent.com/docs/company",
	},
	"credit_note_reconciliations": {
		Name: "credit_note_reconciliations", Path: "credit_note_reconciliations",
		Singular: "credit_note_reconciliation", Plural: "credit_note_reconciliations",
		Doc: "https://dev.freeagent.com/docs/credit_note_reconciliations",
	},
	"credit_notes": {
		Name: "credit_notes", Path: "credit_notes",
		Singular: "credit_note", Plural: "credit_notes",
		Doc: "https://dev.freeagent.com/docs/credit_notes",
	},
	"contacts": {
		Name: "contacts", Path: "contacts",
		Singular: "contact", Plural: "contacts",
		Doc: "https://dev.freeagent.com/docs/contacts",
	},
	"corporation_tax_returns": {
		Name: "corporation_tax_returns", Path: "corporation_tax_returns",
		Singular: "corporation_tax_return", Plural: "corporation_tax_returns",
		Doc: "https://dev.freeagent.com/docs/corporation_tax_returns",
	},

	"email_addresses": {
		Name: "email_addresses", Path: "email_addresses",
		Plural: "email_addresses", Singleton: true, ReadOnly: true,
		Doc: "https://dev.freeagent.com/docs/email_addresses",
	},
	"estimates": {
		Name: "estimates", Path: "estimates",
		Singular: "estimate", Plural: "estimates",
		Doc: "https://dev.freeagent.com/docs/estimates",
	},
	"expenses": {
		Name: "expenses", Path: "expenses",
		Singular: "expense", Plural: "expenses",
		Doc: "https://dev.freeagent.com/docs/expenses",
	},

	"final_accounts_reports": {
		Name: "final_accounts_reports", Path: "final_accounts_reports",
		Singular: "final_accounts_report", Plural: "final_accounts_reports",
		Doc: "https://dev.freeagent.com/docs/final_accounts_reports",
	},

	"hire_purchases": {
		Name: "hire_purchases", Path: "hire_purchases",
		Singular: "hire_purchase", Plural: "hire_purchases",
		ReadOnly: true,
		Doc:      "https://dev.freeagent.com/docs/hire_purchases",
	},

	"income_tax_returns": {
		Name: "income_tax_returns", Path: "self_assessment_returns",
		Singular: "self_assessment_return", Plural: "self_assessment_returns",
		Doc: "https://dev.freeagent.com/docs/income_tax_returns",
	},
	"invoices": {
		Name: "invoices", Path: "invoices",
		Singular: "invoice", Plural: "invoices",
		Doc: "https://dev.freeagent.com/docs/invoices",
	},
	"journal_sets": {
		Name: "journal_sets", Path: "journal_sets",
		Singular: "journal_set", Plural: "journal_sets",
		Doc: "https://dev.freeagent.com/docs/journal_sets",
	},

	"notes": {
		Name: "notes", Path: "notes",
		Singular: "note", Plural: "notes",
		Doc: "https://dev.freeagent.com/docs/notes",
	},

	"payroll": {
		Name: "payroll", Path: "payroll",
		ReadOnly: true, CustomEnvelope: true,
		Doc: "https://dev.freeagent.com/docs/payroll",
	},
	"payroll_profiles": {
		Name: "payroll_profiles", Path: "payroll_profiles",
		ReadOnly: true, CustomEnvelope: true,
		Doc: "https://dev.freeagent.com/docs/payroll_profiles",
	},
	"price_list_items": {
		Name: "price_list_items", Path: "price_list_items",
		Singular: "price_list_item", Plural: "price_list_items",
		Doc: "https://dev.freeagent.com/docs/price_list_items",
	},
	"profit_and_loss": {
		Name: "profit_and_loss", Path: "accounting/profit_and_loss/summary",
		Singleton: true, ReadOnly: true,
		Doc: "https://dev.freeagent.com/docs/profit_and_loss",
	},
	"projects": {
		Name: "projects", Path: "projects",
		Singular: "project", Plural: "projects",
		Doc: "https://dev.freeagent.com/docs/projects",
	},

	"properties": {
		Name: "properties", Path: "properties",
		Singular: "property", Plural: "properties",
		Doc: "https://dev.freeagent.com/docs/properties",
	},

	"recurring_invoices": {
		Name: "recurring_invoices", Path: "recurring_invoices",
		Singular: "recurring_invoice", Plural: "recurring_invoices",
		ReadOnly: true,
		Doc:      "https://dev.freeagent.com/docs/recurring_invoices",
	},

	"sales_tax_periods": {
		Name: "sales_tax_periods", Path: "sales_tax_periods",
		Singular: "sales_tax_period", Plural: "sales_tax_periods",
		Doc: "https://dev.freeagent.com/docs/sales_tax_periods",
	},
	"stock_items": {
		Name: "stock_items", Path: "stock_items",
		Singular: "stock_item", Plural: "stock_items",
		ReadOnly: true,
		Doc:      "https://dev.freeagent.com/docs/stock_items",
	},
	"tasks": {
		Name: "tasks", Path: "tasks",
		Singular: "task", Plural: "tasks",
		Doc: "https://dev.freeagent.com/docs/tasks",
	},
	"timeslips": {
		Name: "timeslips", Path: "timeslips",
		Singular: "timeslip", Plural: "timeslips",
		Doc: "https://dev.freeagent.com/docs/timeslips",
	},

	"transactions": {
		Name: "transactions", Path: "accounting/transactions",
		Singular: "transaction", Plural: "transactions",
		ReadOnly: true,
		Doc:      "https://dev.freeagent.com/docs/transactions",
	},

	"vat_returns": {
		Name: "vat_returns", Path: "vat_returns",
		Singular: "vat_return", Plural: "vat_returns",
		Doc: "https://dev.freeagent.com/docs/vat_returns",
	},
	"trial_balance": {
		Name: "trial_balance", Path: "accounting/trial_balance/summary",
		Singleton: true, ReadOnly: true,
		Doc: "https://dev.freeagent.com/docs/trial_balance",
	},
	"users": {
		Name: "users", Path: "users",
		Singular: "user", Plural: "users",
		Doc: "https://dev.freeagent.com/docs/users",
	},
}

Resources holds the families whose paths have been verified against the upstream documentation. Entries are added as each wave of typed models lands; until then, facli raw reaches any endpoint by path.

Functions

func DefaultTokenPath

func DefaultTokenPath() (string, error)

DefaultTokenPath is where facli keeps credentials when not told otherwise.

func ExpiresIn

func ExpiresIn(t *oauth2.Token, now time.Time) string

ExpiresIn renders a human-readable remaining lifetime for tooling.

func ResourceNames

func ResourceNames() []string

ResourceNames lists the registered families in a stable order.

Types

type APIError

type APIError struct {
	StatusCode int
	Method     string
	URL        string
	Message    string
	Errors     []FieldError
	RetryAfter time.Duration
	RequestID  string
	// Body is the raw response, truncated to errorBodyLimit. It is kept so a
	// caller can log the original when the parsed message proves unhelpful.
	Body []byte
}

APIError is returned for any response with a 4xx or 5xx status.

Example

Classify a failure without matching on message text.

package main

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/alekc/freeagent-sdk/freeagent"
)

func main() {
	err := error(&freeagent.APIError{
		StatusCode: http.StatusUnprocessableEntity,
		Method:     http.MethodPost,
		URL:        "/v2/invoices",
		Errors: []freeagent.FieldError{
			{Field: "dated_on", Message: "can't be blank"},
		},
	})

	if errors.Is(err, freeagent.ErrValidation) {
		var apiErr *freeagent.APIError
		if errors.As(err, &apiErr) {
			for _, fieldErr := range apiErr.Errors {
				fmt.Printf("%s: %s\n", fieldErr.Field, fieldErr.Message)
			}
		}
	}
}
Output:
dated_on: can't be blank

func (*APIError) Error

func (e *APIError) Error() string

Error implements the error interface.

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is maps the status code onto the package sentinels.

type AccountingPeriod

type AccountingPeriod struct {
	StartsOn Date `json:"starts_on,omitzero"`
	EndsOn   Date `json:"ends_on,omitzero"`
}

AccountingPeriod is one entry in a company's annual accounting periods.

type Attachment

type Attachment struct {
	URL ResourceURL `json:"url,omitempty"`

	// Data is the file content, base64 encoded on the wire. Write-only.
	Data []byte `json:"data,omitempty"`

	FileName    string `json:"file_name,omitempty"`
	ContentType string `json:"content_type,omitempty"`
	Description string `json:"description,omitempty"`

	FileSize         int    `json:"file_size,omitempty"`
	ContentSrc       string `json:"content_src,omitempty"`
	ContentSrcMedium string `json:"content_src_medium,omitempty"`
	ContentSrcSmall  string `json:"content_src_small,omitempty"`
	ExpiresAt        Time   `json:"expires_at,omitzero"`
}

Attachment is a file attached to another record.

The same JSON key carries two shapes: on read the API returns the stored file's metadata and time-limited download URLs, and on write it expects the file content. Set Data, FileName and ContentType to upload; everything else is populated by the server.

See https://dev.freeagent.com/docs/attachments

func (Attachment) MarshalJSON

func (a Attachment) MarshalJSON() ([]byte, error)

MarshalJSON enforces the documented size limit at encode time, so an oversized upload fails locally on any resource that carries an attachment rather than after transferring several megabytes.

type AttachmentService

type AttachmentService struct {
	// contains filtered or unexported fields
}

AttachmentService reads and removes attachments. There is no list or create endpoint: attachments are created through the parent record's attachment field, so this service deliberately offers neither.

See https://dev.freeagent.com/docs/attachments

func (*AttachmentService) Delete

func (s *AttachmentService) Delete(ctx context.Context, id int64) (*Response, error)

Delete removes one attachment.

func (*AttachmentService) Get

Get fetches one attachment by numeric id.

func (*AttachmentService) GetURL

GetURL fetches the attachment a payload reference points at.

func (*AttachmentService) Meta

func (s *AttachmentService) Meta() ResourceMeta

Meta returns the resource metadata.

type BalanceSheet

type BalanceSheet struct {
	AccountingPeriodStartDate Date   `json:"accounting_period_start_date,omitzero"`
	AsAtDate                  Date   `json:"as_at_date,omitzero"`
	Currency                  string `json:"currency,omitempty"`

	CapitalAssets      *BalanceSheetCapitalAssets `json:"capital_assets,omitempty"`
	CurrentAssets      *BalanceSheetSection       `json:"current_assets,omitempty"`
	CurrentLiabilities *BalanceSheetSection       `json:"current_liabilities,omitempty"`
	OwnersEquity       *BalanceSheetEquity        `json:"owners_equity,omitempty"`

	NetCurrentAssets  *Decimal `json:"net_current_assets,omitempty"`
	TotalAssets       *Decimal `json:"total_assets,omitempty"`
	TotalOwnersEquity *Decimal `json:"total_owners_equity,omitempty"`
}

BalanceSheet is the balance sheet as at a date.

type BalanceSheetAccount

type BalanceSheetAccount struct {
	Name        string   `json:"name,omitempty"`
	NominalCode string   `json:"nominal_code,omitempty"`
	TotalDebit  *Decimal `json:"total_debit_value,omitempty"`
}

BalanceSheetAccount is one account line.

type BalanceSheetCapitalAssets

type BalanceSheetCapitalAssets struct {
	NetBookValue *Decimal `json:"net_book_value,omitempty"`
}

BalanceSheetCapitalAssets summarises capital assets.

type BalanceSheetEquity

type BalanceSheetEquity struct {
	RetainedProfit *Decimal `json:"retained_profit,omitempty"`
}

BalanceSheetEquity summarises owners' equity.

type BalanceSheetSection

type BalanceSheetSection struct {
	Accounts []BalanceSheetAccount `json:"accounts,omitempty"`
}

BalanceSheetSection groups the accounts under one heading.

type BankAccount

type BankAccount struct {
	URL ResourceURL `json:"url,omitempty"`

	// Type, Name, BankName and OpeningBalance are required on create.
	Type     string `json:"type,omitempty"`
	Name     string `json:"name,omitempty"`
	BankName string `json:"bank_name,omitempty"`
	// OpeningBalance is the balance at the FreeAgent start date.
	OpeningBalance *Decimal `json:"opening_balance,omitempty"`

	// Currency is immutable once the account has transactions.
	Currency string `json:"currency,omitempty"`
	// Status is active or hidden.
	Status           string `json:"status,omitempty"`
	IsPersonal       *bool  `json:"is_personal,omitempty"`
	IsPrimary        *bool  `json:"is_primary,omitempty"`
	BankCode         string `json:"bank_code,omitempty"`
	BankGuessEnabled *bool  `json:"bank_guess_enabled,omitempty"`

	// Standard and credit card accounts.
	AccountNumber     string `json:"account_number,omitempty"`
	SortCode          string `json:"sort_code,omitempty"`
	SecondarySortCode string `json:"secondary_sort_code,omitempty"`
	IBAN              string `json:"iban,omitempty"`
	BIC               string `json:"bic,omitempty"`

	// PayPal accounts.
	Email string `json:"email,omitempty"`

	// Read-only.
	CurrentBalance     *Decimal `json:"current_balance,omitempty"`
	LatestActivityDate Date     `json:"latest_activity_date,omitzero"`
	CreatedAt          Time     `json:"created_at,omitzero"`
	UpdatedAt          Time     `json:"updated_at,omitzero"`

	// Transaction tallies. Undocumented, observed on the live API, and
	// useful for spotting an account with unexplained items waiting.
	TotalCount                    *int `json:"total_count,omitempty"`
	UnexplainedTransactionCount   *int `json:"unexplained_transaction_count,omitempty"`
	MarkedForReviewCount          *int `json:"marked_for_review_count,omitempty"`
	ManuallyAddedTransactionCount *int `json:"manually_added_transaction_count,omitempty"`
}

BankAccount is a bank, credit card or payment-provider account.

Several fields apply only to certain account types: AccountNumber, SortCode, IBAN and BIC to standard accounts, AccountNumber alone to credit cards, and Email to PayPal accounts.

See https://dev.freeagent.com/docs/bank_accounts

type BankAccountService

type BankAccountService struct {
	Collection[BankAccount]
}

BankAccountService covers https://dev.freeagent.com/docs/bank_accounts

type BankFeed

type BankFeed struct {
	URL ResourceURL `json:"url,omitempty"`

	BankAccount ResourceURL `json:"bank_account,omitempty"`
	// State is the feed's current status, for example enabled.
	State string `json:"state,omitempty"`
	// FeedType is api or open_banking.
	FeedType        string `json:"feed_type,omitempty"`
	BankServiceName string `json:"bank_service_name,omitempty"`
	// SCAExpiresAt is when strong customer authentication lapses and the feed
	// needs reconnecting. API feeds only.
	SCAExpiresAt Time `json:"sca_expires_at,omitzero"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

BankFeed is a live connection importing transactions into a bank account.

Read-only: a feed is established through the FreeAgent interface or a banking partner, not through this API.

See https://dev.freeagent.com/docs/bank_feeds

type BankFeedService

type BankFeedService struct {
	ReadCollection[BankFeed]
}

BankFeedService covers https://dev.freeagent.com/docs/bank_feeds

type BankTransaction

type BankTransaction struct {
	URL ResourceURL `json:"url,omitempty"`

	BankAccount ResourceURL `json:"bank_account,omitempty"`
	// Amount is in the company's native currency.
	Amount          *Decimal `json:"amount,omitempty"`
	DatedOn         Date     `json:"dated_on,omitzero"`
	Description     string   `json:"description,omitempty"`
	FullDescription string   `json:"full_description,omitempty"`
	// TransactionID is the bank's own identifier, also known as fit_id.
	TransactionID string `json:"transaction_id,omitempty"`

	// Read-only.
	UnexplainedAmount           *Decimal                     `json:"unexplained_amount,omitempty"`
	IsManual                    *bool                        `json:"is_manual,omitempty"`
	MatchingTransactionsCount   *int                         `json:"matching_transactions_count,omitempty"`
	BankTransactionExplanations []BankTransactionExplanation `json:"bank_transaction_explanations,omitempty"`
	UploadedAt                  Time                         `json:"uploaded_at,omitzero"`
	CreatedAt                   Time                         `json:"created_at,omitzero"`
	UpdatedAt                   Time                         `json:"updated_at,omitzero"`
}

BankTransaction is a line on a bank statement or feed.

Individual transactions are not created, updated or deleted through the API: they arrive by statement upload or bank feed, which is why this family exposes only reads plus UploadStatement.

See https://dev.freeagent.com/docs/bank_transactions

type BankTransactionExplanation

type BankTransactionExplanation struct {
	URL ResourceURL `json:"url,omitempty"`

	// One of BankAccount or BankTransaction is required.
	BankAccount     ResourceURL `json:"bank_account,omitempty"`
	BankTransaction ResourceURL `json:"bank_transaction,omitempty"`

	DatedOn      Date        `json:"dated_on,omitzero"`
	GrossValue   *Decimal    `json:"gross_value,omitempty"`
	Description  string      `json:"description,omitempty"`
	Category     ResourceURL `json:"category,omitempty"`
	ChequeNumber string      `json:"cheque_number,omitempty"`

	SalesTaxRate  *Decimal `json:"sales_tax_rate,omitempty"`
	SalesTaxValue *Decimal `json:"sales_tax_value,omitempty"`
	// SalesTaxStatus is TAXABLE, EXEMPT or OUT_OF_SCOPE.
	SalesTaxStatus       string   `json:"sales_tax_status,omitempty"`
	SecondSalesTaxRate   *Decimal `json:"second_sales_tax_rate,omitempty"`
	SecondSalesTaxValue  *Decimal `json:"second_sales_tax_value,omitempty"`
	SecondSalesTaxStatus string   `json:"second_sales_tax_status,omitempty"`
	// ECStatus is UK/Non-EC, EC Goods, EC Services, Reverse Charge or
	// EC VAT MOSS. PlaceOfSupply is required for EC VAT MOSS.
	ECStatus      string `json:"ec_status,omitempty"`
	PlaceOfSupply string `json:"place_of_supply,omitempty"`

	// Payments and refunds.
	Project          ResourceURL `json:"project,omitempty"`
	RebillType       string      `json:"rebill_type,omitempty"`
	RebillFactor     *Decimal    `json:"rebill_factor,omitempty"`
	ReceiptReference string      `json:"receipt_reference,omitempty"`

	// Invoice and bill settlement. ForeignCurrencyValue applies when the
	// settled document is in another currency.
	PaidInvoice          ResourceURL `json:"paid_invoice,omitempty"`
	PaidBill             ResourceURL `json:"paid_bill,omitempty"`
	ForeignCurrencyValue *Decimal    `json:"foreign_currency_value,omitempty"`

	// Money paid to or from a user.
	PaidUser ResourceURL `json:"paid_user,omitempty"`

	// Transfers between accounts.
	TransferBankAccount ResourceURL `json:"transfer_bank_account,omitempty"`

	// Stock movements.
	StockItem             ResourceURL `json:"stock_item,omitempty"`
	StockAlteringQuantity *int        `json:"stock_altering_quantity,omitempty"`

	// Capital assets. DisposedAsset is required for a disposal.
	DisposedAsset ResourceURL `json:"disposed_asset,omitempty"`

	// UK unincorporated landlords.
	Property ResourceURL `json:"property,omitempty"`
	// Opening balances against an initial debtor or creditor category.
	DirectContact ResourceURL `json:"direct_contact,omitempty"`

	Attachment *Attachment `json:"attachment,omitempty"`

	// Read-only.
	Type                      string      `json:"type,omitempty"`
	CapitalAsset              ResourceURL `json:"capital_asset,omitempty"`
	LinkedTransferExplanation ResourceURL `json:"linked_transfer_explanation,omitempty"`
	LinkedTransferAccount     ResourceURL `json:"linked_transfer_account,omitempty"`
	MarkedForReview           *bool       `json:"marked_for_review,omitempty"`
	IsMoneyIn                 *bool       `json:"is_money_in,omitempty"`
	IsMoneyOut                *bool       `json:"is_money_out,omitempty"`
	IsMoneyPaidToUser         *bool       `json:"is_money_paid_to_user,omitempty"`
	IsLocked                  *bool       `json:"is_locked,omitempty"`
	IsDeletable               *bool       `json:"is_deletable,omitempty"`
	LockedAttributes          []string    `json:"locked_attributes,omitempty"`
	LockedReason              string      `json:"locked_reason,omitempty"`
	UpdatedAt                 Time        `json:"updated_at,omitzero"`
}

BankTransactionExplanation records what a bank transaction was for.

Which fields are required depends on the kind of explanation: PaidInvoice for an invoice receipt, PaidBill for a bill payment, TransferBankAccount for a transfer, and so on. The grouped comments below follow the upstream documentation.

See https://dev.freeagent.com/docs/bank_transaction_explanations

type BankTransactionExplanationService

type BankTransactionExplanationService struct {
	Collection[BankTransactionExplanation]
}

BankTransactionExplanationService covers https://dev.freeagent.com/docs/bank_transaction_explanations

func (*BankTransactionExplanationService) All

All is unavailable without a bank account. Use AllForAccount.

func (*BankTransactionExplanationService) AllForAccount

AllForAccount iterates every explanation for a bank account.

func (*BankTransactionExplanationService) List

List is unavailable without a bank account. The API requires the bank_account parameter, so this shadows the inherited List rather than letting it fail remotely. Use ListForAccount.

func (*BankTransactionExplanationService) ListForAccount

ListForAccount fetches one page of explanations for a bank account.

type BankTransactionService

type BankTransactionService struct {
	ReadCollection[BankTransaction]
}

BankTransactionService covers https://dev.freeagent.com/docs/bank_transactions

func (*BankTransactionService) All

All is unavailable without a bank account. Use AllForAccount.

func (*BankTransactionService) AllForAccount

AllForAccount iterates every transaction for a bank account.

func (*BankTransactionService) List

List is unavailable without a bank account. The API requires the bank_account parameter, so this shadows the inherited List rather than letting it fail remotely. Use ListForAccount.

func (*BankTransactionService) ListForAccount

func (s *BankTransactionService) ListForAccount(ctx context.Context, account ResourceURL, opts *ListOptions) ([]BankTransaction, *Response, error)

ListForAccount fetches one page of transactions for a bank account.

func (*BankTransactionService) UploadStatement

func (s *BankTransactionService) UploadStatement(ctx context.Context, account ResourceURL, lines []StatementLine) (*Response, error)

UploadStatement posts statement lines to a bank account. This is the only way transactions enter FreeAgent through the API.

Import is asynchronous. The call returns before the lines are queryable, and the delay has been observed to range from under a second to most of a minute, so a read straight afterwards will often come back empty. Poll ListForAccount until the lines appear rather than treating the 200 as confirmation that they exist.

type Bill

type Bill struct {
	URL ResourceURL `json:"url,omitempty"`

	// Contact is required.
	Contact  ResourceURL `json:"contact,omitempty"`
	Project  ResourceURL `json:"project,omitempty"`
	Property ResourceURL `json:"property,omitempty"`

	Reference string `json:"reference,omitempty"`
	DatedOn   Date   `json:"dated_on,omitzero"`
	DueOn     Date   `json:"due_on,omitzero"`
	Currency  string `json:"currency,omitempty"`
	Comments  string `json:"comments,omitempty"`

	// InputTotalValuesIncTax defaults to false for the native currency and
	// true otherwise.
	InputTotalValuesIncTax *bool `json:"input_total_values_inc_tax,omitempty"`
	IsPaidByHirePurchase   *bool `json:"is_paid_by_hire_purchase,omitempty"`
	// ECStatus is UK/Non-EC, EC Goods, EC Services or Reverse Charge.
	ECStatus string `json:"ec_status,omitempty"`

	// Rebilling. RebillFactor is required when RebillType is markup or price.
	RebillType      string      `json:"rebill_type,omitempty"`
	RebillFactor    *Decimal    `json:"rebill_factor,omitempty"`
	RebillToProject ResourceURL `json:"rebill_to_project,omitempty"`

	// Recurring is Weekly, Two Weekly, Four Weekly, Two Monthly, Quarterly,
	// Biannually, Annually or 2-Yearly.
	Recurring        string `json:"recurring,omitempty"`
	RecurringEndDate Date   `json:"recurring_end_date,omitzero"`

	// CISDeductionBand is cis_gross, cis_standard or cis_higher.
	CISDeductionBand string `json:"cis_deduction_band,omitempty"`

	Attachment *Attachment `json:"attachment,omitempty"`
	BillItems  []BillItem  `json:"bill_items,omitempty"`

	// Read-only.
	Status               string   `json:"status,omitempty"`
	LongStatus           string   `json:"long_status,omitempty"`
	PaidOn               Date     `json:"paid_on,omitzero"`
	TotalValue           *Decimal `json:"total_value,omitempty"`
	NetValue             *Decimal `json:"net_value,omitempty"`
	DueValue             *Decimal `json:"due_value,omitempty"`
	NativeDueValue       *Decimal `json:"native_due_value,omitempty"`
	ExchangeRate         *Decimal `json:"exchange_rate,omitempty"`
	SalesTaxValue        *Decimal `json:"sales_tax_value,omitempty"`
	SecondSalesTaxValue  *Decimal `json:"second_sales_tax_value,omitempty"`
	CISDeductionRate     *Decimal `json:"cis_deduction_rate,omitempty"`
	CISDeduction         *Decimal `json:"cis_deduction,omitempty"`
	CISDeductionSuffered *Decimal `json:"cis_deduction_suffered,omitempty"`
	CreatedAt            Time     `json:"created_at,omitzero"`
	UpdatedAt            Time     `json:"updated_at,omitzero"`
}

Bill is a purchase invoice owed to a supplier.

See https://dev.freeagent.com/docs/bills

type BillItem

type BillItem struct {
	URL ResourceURL `json:"url,omitempty"`
	// Destroy set to 1 removes the line on a write.
	Destroy *int `json:"_destroy,omitempty"`

	// Category is required.
	Category    ResourceURL `json:"category,omitempty"`
	Description string      `json:"description,omitempty"`
	Project     ResourceURL `json:"project,omitempty"`

	// TotalValue includes tax; TotalValueExTax is the alternative form. Send
	// one or the other, not both.
	TotalValue      *Decimal `json:"total_value,omitempty"`
	TotalValueExTax *Decimal `json:"total_value_ex_tax,omitempty"`

	ManualSalesTaxAmount *Decimal `json:"manual_sales_tax_amount,omitempty"`
	SalesTaxRate         *Decimal `json:"sales_tax_rate,omitempty"`
	// SalesTaxStatus is TAXABLE, EXEMPT or OUT_OF_SCOPE.
	SalesTaxStatus       string   `json:"sales_tax_status,omitempty"`
	SecondSalesTaxRate   *Decimal `json:"second_sales_tax_rate,omitempty"`
	SecondSalesTaxStatus string   `json:"second_sales_tax_status,omitempty"`

	// Unit is -no unit-, Hours, Days, Weeks, Months, Years, Products,
	// Services, Training or Stock.
	Unit     string   `json:"unit,omitempty"`
	Quantity *Decimal `json:"quantity,omitempty"`

	StockItem             ResourceURL `json:"stock_item,omitempty"`
	StockAlteringQuantity *Decimal    `json:"stock_altering_quantity,omitempty"`

	CISDeductionRate *Decimal `json:"cis_deduction_rate,omitempty"`

	// Read-only.
	Bill                 ResourceURL `json:"bill,omitempty"`
	CapitalAsset         ResourceURL `json:"capital_asset,omitempty"`
	StockItemDescription string      `json:"stock_item_description,omitempty"`
}

BillItem is one line on a bill. A bill accepts up to 40 items.

type BillService

type BillService struct {
	Collection[Bill]
}

BillService covers https://dev.freeagent.com/docs/bills

type CISBand

type CISBand struct {
	// Name is cis_gross, cis_standard or cis_higher.
	Name string `json:"name,omitempty"`
	// DeductionRate is a fraction, so 20% arrives as "0.2".
	DeductionRate        *Decimal `json:"deduction_rate,omitempty"`
	IncomeDescription    string   `json:"income_description,omitempty"`
	DeductionDescription string   `json:"deduction_description,omitempty"`
	NominalCode          string   `json:"nominal_code,omitempty"`
}

CISBand is one Construction Industry Scheme deduction band.

UK companies enrolled in CIS for subcontractors only; the list is empty otherwise.

See https://dev.freeagent.com/docs/cis_bands

type CISBandService

type CISBandService struct {
	// contains filtered or unexported fields
}

CISBandService covers https://dev.freeagent.com/docs/cis_bands

The envelope key is available_bands, not cis_bands, so this does not fit the generic collection.

func (*CISBandService) List

func (s *CISBandService) List(ctx context.Context) ([]CISBand, *Response, error)

List returns the bands available to the company.

func (*CISBandService) Meta

func (s *CISBandService) Meta() ResourceMeta

Meta returns the resource metadata.

type CapitalAsset

type CapitalAsset struct {
	URL ResourceURL `json:"url,omitempty"`

	Description string `json:"description,omitempty"`
	// AssetType is a capital asset type name, not a URL.
	AssetType   string `json:"asset_type,omitempty"`
	PurchasedOn Date   `json:"purchased_on,omitzero"`
	DisposedOn  Date   `json:"disposed_on,omitzero"`

	DepreciationProfile *DepreciationProfile `json:"depreciation_profile,omitempty"`
	// AssetLifeYears is superseded by DepreciationProfile.
	//
	// Deprecated: use DepreciationProfile.AssetLifeYears.
	AssetLifeYears *int `json:"asset_life_years,omitempty"`

	// CapitalAssetHistory is only populated when include_history=true.
	CapitalAssetHistory []CapitalAssetHistoryEntry `json:"capital_asset_history,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

CapitalAsset is a purchase written down over time.

The API exposes reads only: assets come into being through the expense, bill or bank explanation that bought them, which is why this family embeds ReadCollection.

See https://dev.freeagent.com/docs/capital_assets

type CapitalAssetHistoryEntry

type CapitalAssetHistoryEntry struct {
	// Type is purchase, depreciation, annual_investment_allowance or
	// disposal.
	Type        string      `json:"type,omitempty"`
	Description string      `json:"description,omitempty"`
	Date        Date        `json:"date,omitzero"`
	Value       *Decimal    `json:"value,omitempty"`
	TaxValue    *Decimal    `json:"tax_value,omitempty"`
	Link        ResourceURL `json:"link,omitempty"`
}

CapitalAssetHistoryEntry is one event in an asset's life.

type CapitalAssetService

type CapitalAssetService struct {
	ReadCollection[CapitalAsset]
}

CapitalAssetService covers https://dev.freeagent.com/docs/capital_assets

func (*CapitalAssetService) GetWithHistory

func (s *CapitalAssetService) GetWithHistory(ctx context.Context, id int64) (*CapitalAsset, *Response, error)

GetWithHistory is Get with include_history=true.

func (*CapitalAssetService) ListWithHistory

func (s *CapitalAssetService) ListWithHistory(ctx context.Context, opts *ListOptions) ([]CapitalAsset, *Response, error)

ListWithHistory is List with include_history=true, which populates CapitalAssetHistory. It is off by default because the history is large.

type CapitalAssetType

type CapitalAssetType struct {
	URL ResourceURL `json:"url,omitempty"`

	Name string `json:"name,omitempty"`

	// Read-only.
	SystemDefault *bool `json:"system_default,omitempty"`
	CreatedAt     Time  `json:"created_at,omitzero"`
	UpdatedAt     Time  `json:"updated_at,omitzero"`
}

CapitalAssetType names a class of capital asset. Four are seeded by FreeAgent and marked SystemDefault; the rest are user-created and are the only ones that may be changed or removed.

See https://dev.freeagent.com/docs/capital_asset_types

type CapitalAssetTypeService

type CapitalAssetTypeService struct {
	Collection[CapitalAssetType]
}

CapitalAssetTypeService covers https://dev.freeagent.com/docs/capital_asset_types

type Cashflow

type Cashflow struct {
	From     Date             `json:"from,omitzero"`
	To       Date             `json:"to,omitzero"`
	Incoming *CashflowSection `json:"incoming,omitempty"`
	Outgoing *CashflowSection `json:"outgoing,omitempty"`
	Balance  *Decimal         `json:"balance,omitempty"`
}

Cashflow is money in and out over a period, bucketed by month.

It reports history only: dates in the future come back as zero rather than as a forecast.

type CashflowMonth

type CashflowMonth struct {
	Month int      `json:"month,omitempty"`
	Year  int      `json:"year,omitempty"`
	Total *Decimal `json:"total,omitempty"`
}

CashflowMonth is one month's bucket.

type CashflowSection

type CashflowSection struct {
	Total  *Decimal        `json:"total,omitempty"`
	Months []CashflowMonth `json:"months,omitempty"`
}

CashflowSection is one direction of the cashflow report.

type Category

type Category struct {
	URL ResourceURL `json:"url,omitempty"`

	Description string `json:"description,omitempty"`
	// NominalCode identifies the category. On create it must be free and
	// inside the range the group allows: admin expenses are 200 to 399.
	NominalCode string `json:"nominal_code,omitempty"`
	// GroupDescription is present on income and spending categories. It is
	// read-only in practice: writes use CategoryGroup instead.
	GroupDescription string `json:"group_description,omitempty"`
	// CategoryGroup selects the group on create and is required there, even
	// though the documented attribute list does not mention it. Omitting it
	// returns 422 "” is not a valid category_group". Values are the group
	// names without the _categories suffix: admin_expenses, cost_of_sales,
	// income, general.
	CategoryGroup string `json:"category_group,omitempty"`
	// AllowableForTax appears on spending categories only.
	AllowableForTax *bool `json:"allowable_for_tax,omitempty"`
	// TaxReportingName is where the category lands in statutory accounts. It
	// is required on create and only accepts values from a fixed list, which
	// is not published: read one off an existing category in the same group.
	TaxReportingName string `json:"tax_reporting_name,omitempty"`
	// AutoSalesTaxRate is Outside scope, Zero rate, Reduced rate,
	// Standard rate or Exempt.
	AutoSalesTaxRate string `json:"auto_sales_tax_rate,omitempty"`

	// Sub-account links. Each is present only on the matching code range.
	BankAccount      ResourceURL `json:"bank_account,omitempty"`
	CapitalAssetType ResourceURL `json:"capital_asset_type,omitempty"`
	StockItem        ResourceURL `json:"stock_item,omitempty"`
	HirePurchase     ResourceURL `json:"hire_purchase,omitempty"`
	User             ResourceURL `json:"user,omitempty"`

	// Group is the envelope the category arrived in. It is filled in by this
	// library rather than sent by the API, and is never written back.
	Group string `json:"-"`
}

Category is an accounting category, identified by its nominal code rather than a numeric id.

See https://dev.freeagent.com/docs/categories

type CategoryGroups

type CategoryGroups struct {
	AdminExpenses []Category `json:"admin_expenses_categories,omitempty"`
	CostOfSales   []Category `json:"cost_of_sales_categories,omitempty"`
	Income        []Category `json:"income_categories,omitempty"`
	General       []Category `json:"general_categories,omitempty"`
}

CategoryGroups is the grouped response the categories endpoint returns. Unlike every other collection there is no flat list, so this mirrors the envelope rather than pretending otherwise.

func (*CategoryGroups) Flatten

func (g *CategoryGroups) Flatten() []Category

Flatten returns every category with Group set, for callers that want one list rather than four.

type CategoryService

type CategoryService struct {
	// contains filtered or unexported fields
}

CategoryService covers https://dev.freeagent.com/docs/categories

Categories do not fit the generic collection: results come back grouped under four keys with no flat list, and records are addressed by nominal code rather than a numeric id.

func (*CategoryService) Create

func (s *CategoryService) Create(ctx context.Context, in *Category) (*Category, *Response, error)

Create adds a user-defined category.

func (*CategoryService) Delete

func (s *CategoryService) Delete(ctx context.Context, nominalCode string) (*Response, error)

Delete removes a category. Only user-created categories with no items can be deleted.

func (*CategoryService) Get

func (s *CategoryService) Get(ctx context.Context, nominalCode string) (*Category, *Response, error)

Get fetches one category by nominal code. The reply is nested under whichever group the category belongs to, so the group is resolved here and recorded on the result.

func (*CategoryService) List

func (s *CategoryService) List(ctx context.Context, subAccounts bool) (*CategoryGroups, *Response, error)

List returns the categories grouped as the API reports them. Set subAccounts to fetch sub-accounts in place of their parent accounts.

func (*CategoryService) Meta

func (s *CategoryService) Meta() ResourceMeta

Meta returns the resource metadata.

func (*CategoryService) Update

func (s *CategoryService) Update(ctx context.Context, nominalCode string, in *Category) (*Category, *Response, error)

Update changes a category. Only categories with no items can be updated.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a FreeAgent API client. It is safe for concurrent use.

func NewClient

func NewClient(opts ...Option) (*Client, error)

NewClient builds a client. Exactly one of WithTokenSource or WithoutAuth is required.

func (*Client) BaseURL

func (c *Client) BaseURL() *url.URL

BaseURL returns the configured endpoint.

func (*Client) Raw

func (c *Client) Raw(ctx context.Context, method, path string, query url.Values, body any) ([]byte, *Response, error)

Raw issues an arbitrary request against the API root and returns the undecoded response body. It exists so tooling can reach endpoints that have no typed model yet, and so a caller can inspect fields this library does not model. path is relative to the API root, for example "invoices/123".

func (*Client) RawURL

func (c *Client) RawURL(ctx context.Context, method string, ref ResourceURL, query url.Values, body any) ([]byte, *Response, error)

RawURL is Raw for a payload reference. The URL must be on the client's own host; see pathForURL for why that is enforced.

type Collection

type Collection[T any] struct {
	ReadCollection[T]
}

Collection adds the write verbs to ReadCollection. Resource services embed one of the two and add only the endpoints that do not fit the shape, which is what keeps 45 resource families from turning into 225 near-identical methods.

func (*Collection[T]) Create

func (c *Collection[T]) Create(ctx context.Context, in *T) (*T, *Response, error)

Create posts a new record and returns the server's version of it.

func (*Collection[T]) Delete

func (c *Collection[T]) Delete(ctx context.Context, id int64) (*Response, error)

Delete removes a record by numeric id.

func (*Collection[T]) Update

func (c *Collection[T]) Update(ctx context.Context, id int64, in *T) (*T, *Response, error)

Update replaces a record by numeric id.

type Company

type Company struct {
	URL ResourceURL `json:"url,omitempty"`
	// ID is typed as an integer in the documentation while the example on the
	// same page returns it quoted. The live API sends an unquoted number, so
	// the example is the stale one, but the type stays lenient rather than
	// betting on that staying true.
	ID Int64 `json:"id,omitempty"`

	Name      string `json:"name,omitempty"`
	Subdomain string `json:"subdomain,omitempty"`
	// Locale is undocumented but returned by the live API.
	Locale string `json:"locale,omitempty"`
	// Type is one of UkLimitedCompany, UkLimitedLiabilityPartnership,
	// UkPartnership, UkSoleTrader, UkUnincorporatedLandlord,
	// UsLimitedLiabilityCompany, UsPartnership, UsSoleProprietor, UsCCorp,
	// UsSCorp or UniversalCompany.
	Type     string `json:"type,omitempty"`
	Currency string `json:"currency,omitempty"`
	// MileageUnits is miles or kilometers.
	MileageUnits string `json:"mileage_units,omitempty"`

	CompanyStartDate        Date               `json:"company_start_date,omitzero"`
	TradingStartDate        Date               `json:"trading_start_date,omitzero"`
	FirstAccountingYearEnd  Date               `json:"first_accounting_year_end,omitzero"`
	FreeAgentStartDate      Date               `json:"freeagent_start_date,omitzero"`
	AnnualAccountingPeriods []AccountingPeriod `json:"annual_accounting_periods,omitempty"`

	Address1 string `json:"address1,omitempty"`
	Address2 string `json:"address2,omitempty"`
	Address3 string `json:"address3,omitempty"`
	Town     string `json:"town,omitempty"`
	Region   string `json:"region,omitempty"`
	Postcode string `json:"postcode,omitempty"`
	Country  string `json:"country,omitempty"`

	CompanyRegistrationNumber string `json:"company_registration_number,omitempty"`
	ContactEmail              string `json:"contact_email,omitempty"`
	ContactPhone              string `json:"contact_phone,omitempty"`
	Website                   string `json:"website,omitempty"`
	BusinessType              string `json:"business_type,omitempty"`
	BusinessCategory          string `json:"business_category,omitempty"`
	// ShortDateFormat is one of "dd mmm yy", "dd-mm-yyyy", "mm/dd/yyyy" or
	// "yyyy-mm-dd".
	ShortDateFormat string `json:"short_date_format,omitempty"`

	SalesTaxName                        string    `json:"sales_tax_name,omitempty"`
	SalesTaxRegistrationNumber          string    `json:"sales_tax_registration_number,omitempty"`
	SalesTaxRegistrationStatus          string    `json:"sales_tax_registration_status,omitempty"`
	SalesTaxEffectiveDate               Date      `json:"sales_tax_effective_date,omitzero"`
	SalesTaxIsValueAdded                *bool     `json:"sales_tax_is_value_added,omitempty"`
	SalesTaxDeregistrationEffectiveDate Date      `json:"sales_tax_deregistration_effective_date,omitzero"`
	SalesTaxRates                       []Decimal `json:"sales_tax_rates,omitempty"`
	// Undocumented, observed on the live API.
	ECVATReportingEnabled           *bool `json:"ec_vat_reporting_enabled,omitempty"`
	SupportsAutoSalesTaxOnPurchases *bool `json:"supports_auto_sales_tax_on_purchases,omitempty"`

	// Universal and US accounts only.
	SecondSalesTaxName       string    `json:"second_sales_tax_name,omitempty"`
	SecondSalesTaxRates      []Decimal `json:"second_sales_tax_rates,omitempty"`
	SecondSalesTaxIsCompound *bool     `json:"second_sales_tax_is_compound,omitempty"`

	// UK VAT accounts only. InitialVATBasis is Invoice or Cash.
	VATFirstReturnPeriodEndsOn Date   `json:"vat_first_return_period_ends_on,omitzero"`
	InitialVATBasis            string `json:"initial_vat_basis,omitempty"`
	InitiallyOnFRS             *bool  `json:"initially_on_frs,omitempty"`
	InitialVATFRSType          string `json:"initial_vat_frs_type,omitempty"`

	// Construction Industry Scheme. CISEnabled and CISSubcontractor are
	// aliases of each other in the API.
	CISEnabled       *bool `json:"cis_enabled,omitempty"`
	CISSubcontractor *bool `json:"cis_subcontractor,omitempty"`
	CISContractor    *bool `json:"cis_contractor,omitempty"`

	LockedAttributes []string `json:"locked_attributes,omitempty"`
	CreatedAt        Time     `json:"created_at,omitzero"`
	UpdatedAt        Time     `json:"updated_at,omitzero"`
}

Company is the account's own details. It is a singleton: there is no collection and no id segment.

See https://dev.freeagent.com/docs/company

type CompanyService

type CompanyService struct {
	Reader[Company]
}

CompanyService covers https://dev.freeagent.com/docs/company

func (*CompanyService) BusinessCategories

func (s *CompanyService) BusinessCategories(ctx context.Context) ([]string, *Response, error)

BusinessCategories lists the business categories the account may use.

func (*CompanyService) TaxTimeline

func (s *CompanyService) TaxTimeline(ctx context.Context) ([]TaxTimelineItem, *Response, error)

TaxTimeline returns upcoming tax events. It needs the Tax, Accounting and Users access level.

type Contact

type Contact struct {
	URL ResourceURL `json:"url,omitempty"`

	FirstName        string `json:"first_name,omitempty"`
	LastName         string `json:"last_name,omitempty"`
	OrganisationName string `json:"organisation_name,omitempty"`

	Email        string `json:"email,omitempty"`
	BillingEmail string `json:"billing_email,omitempty"`
	PhoneNumber  string `json:"phone_number,omitempty"`
	Mobile       string `json:"mobile,omitempty"`

	Address1 string `json:"address1,omitempty"`
	Address2 string `json:"address2,omitempty"`
	Address3 string `json:"address3,omitempty"`
	Town     string `json:"town,omitempty"`
	Region   string `json:"region,omitempty"`
	Postcode string `json:"postcode,omitempty"`
	Country  string `json:"country,omitempty"`

	// Status is Active or Hidden.
	Status string `json:"status,omitempty"`
	// Locale is one of the language codes FreeAgent supports for invoices.
	Locale string `json:"locale,omitempty"`
	// ChargeSalesTax is Auto, Always or Never.
	ChargeSalesTax             string `json:"charge_sales_tax,omitempty"`
	SalesTaxRegistrationNumber string `json:"sales_tax_registration_number,omitempty"`
	ContactNameOnInvoices      *bool  `json:"contact_name_on_invoices,omitempty"`
	UsesContactInvoiceSequence *bool  `json:"uses_contact_invoice_sequence,omitempty"`
	DefaultPaymentTermsInDays  *int   `json:"default_payment_terms_in_days,omitempty"`

	// Construction Industry Scheme. CISDeductionRate is required when
	// IsCISSubcontractor is true, and is one of cis_gross, cis_standard or
	// cis_higher.
	IsCISSubcontractor              *bool  `json:"is_cis_subcontractor,omitempty"`
	CISDeductionRate                string `json:"cis_deduction_rate,omitempty"`
	UniqueTaxReference              string `json:"unique_tax_reference,omitempty"`
	SubcontractorVerificationNumber string `json:"subcontractor_verification_number,omitempty"`

	// Read-only.
	AccountBalance          *Decimal `json:"account_balance,omitempty"`
	ActiveProjectsCount     Int64    `json:"active_projects_count,omitempty"`
	DirectDebitMandateState string   `json:"direct_debit_mandate_state,omitempty"`
	CreatedAt               Time     `json:"created_at,omitzero"`
	UpdatedAt               Time     `json:"updated_at,omitzero"`
}

Contact is a client or supplier.

Either OrganisationName, or FirstName and LastName, is required. Several fields need the Contacts and Projects permission and are absent otherwise.

See https://dev.freeagent.com/docs/contacts

type ContactService

type ContactService struct {
	Collection[Contact]
}

ContactService covers https://dev.freeagent.com/docs/contacts

type CorporationTaxReturn

type CorporationTaxReturn struct {
	URL ResourceURL `json:"url,omitempty"`

	PeriodStartsOn Date `json:"period_starts_on,omitzero"`
	PeriodEndsOn   Date `json:"period_ends_on,omitzero"`
	FilingDueOn    Date `json:"filing_due_on,omitzero"`

	// FilingStatus is draft, unfiled, pending, rejected, filed or
	// marked_as_filed.
	FilingStatus   string `json:"filing_status,omitempty"`
	FiledAt        Time   `json:"filed_at,omitzero"`
	FiledReference string `json:"filed_reference,omitempty"`

	AmountDue *Decimal `json:"amount_due,omitempty"`
	// PaymentDueOn and PaymentStatus sit on the return itself here, rather
	// than in a payments array as VAT and income tax have them.
	PaymentDueOn  Date   `json:"payment_due_on,omitzero"`
	PaymentStatus string `json:"payment_status,omitempty"`
}

CorporationTaxReturn is one corporation tax period.

See https://dev.freeagent.com/docs/corporation_tax_returns

type CorporationTaxReturnService

type CorporationTaxReturnService struct {
	// contains filtered or unexported fields
}

CorporationTaxReturnService covers https://dev.freeagent.com/docs/corporation_tax_returns

func (*CorporationTaxReturnService) Get

Get fetches one period by its end date.

func (*CorporationTaxReturnService) List

List returns every corporation tax period.

func (*CorporationTaxReturnService) MarkAsFiled

func (s *CorporationTaxReturnService) MarkAsFiled(ctx context.Context, periodEndsOn Date) (*CorporationTaxReturn, *Response, error)

MarkAsFiled records the return as filed. Needs Full Access.

func (*CorporationTaxReturnService) MarkAsPaid

func (s *CorporationTaxReturnService) MarkAsPaid(ctx context.Context, periodEndsOn Date) (*CorporationTaxReturn, *Response, error)

MarkAsPaid records the tax as paid. Unlike VAT, the payment is a property of the return rather than a dated entry, so there is no payment date.

func (*CorporationTaxReturnService) MarkAsUnfiled

func (s *CorporationTaxReturnService) MarkAsUnfiled(ctx context.Context, periodEndsOn Date) (*CorporationTaxReturn, *Response, error)

MarkAsUnfiled reverses MarkAsFiled.

func (*CorporationTaxReturnService) MarkAsUnpaid

func (s *CorporationTaxReturnService) MarkAsUnpaid(ctx context.Context, periodEndsOn Date) (*CorporationTaxReturn, *Response, error)

MarkAsUnpaid reverses MarkAsPaid.

func (*CorporationTaxReturnService) Meta

func (s *CorporationTaxReturnService) Meta() ResourceMeta

Meta returns the resource metadata.

type CreditNote

type CreditNote struct {
	URL ResourceURL `json:"url,omitempty"`

	// Contact is required.
	Contact     ResourceURL `json:"contact,omitempty"`
	Project     ResourceURL `json:"project,omitempty"`
	Property    ResourceURL `json:"property,omitempty"`
	BankAccount ResourceURL `json:"bank_account,omitempty"`

	Reference string `json:"reference,omitempty"`
	// DatedOn is required.
	DatedOn Date `json:"dated_on,omitzero"`
	DueOn   Date `json:"due_on,omitzero"`
	// PaymentTermsInDays is required; zero means due on receipt.
	PaymentTermsInDays *int   `json:"payment_terms_in_days,omitempty"`
	Currency           string `json:"currency,omitempty"`

	// Status is Draft, Open, Overdue, Refunded or Written-off, and is driven
	// by the transitions rather than set directly.
	Status string `json:"status,omitempty"`

	Comments          string   `json:"comments,omitempty"`
	DiscountPercent   *Decimal `json:"discount_percent,omitempty"`
	ClientContactName string   `json:"client_contact_name,omitempty"`
	PaymentTerms      string   `json:"payment_terms,omitempty"`
	POReference       string   `json:"po_reference,omitempty"`
	OmitHeader        *bool    `json:"omit_header,omitempty"`
	ShowProjectName   *bool    `json:"show_project_name,omitempty"`

	// ECStatus is UK/Non-EC, EC Goods, EC Services, Reverse Charge or
	// EC VAT MOSS.
	ECStatus      string `json:"ec_status,omitempty"`
	PlaceOfSupply string `json:"place_of_supply,omitempty"`

	// Construction Industry Scheme.
	CISRate              string   `json:"cis_rate,omitempty"`
	CISDeductionRate     *Decimal `json:"cis_deduction_rate,omitempty"`
	CISDeduction         *Decimal `json:"cis_deduction,omitempty"`
	CISDeductionSuffered *Decimal `json:"cis_deduction_suffered,omitempty"`

	CreditNoteItems []CreditNoteItem `json:"credit_note_items,omitempty"`

	// Read-only.
	LongStatus          string   `json:"long_status,omitempty"`
	NetValue            *Decimal `json:"net_value,omitempty"`
	SalesTaxValue       *Decimal `json:"sales_tax_value,omitempty"`
	SecondSalesTaxValue *Decimal `json:"second_sales_tax_value,omitempty"`
	TotalValue          *Decimal `json:"total_value,omitempty"`
	RefundedValue       *Decimal `json:"refunded_value,omitempty"`
	DueValue            *Decimal `json:"due_value,omitempty"`
	ExchangeRate        *Decimal `json:"exchange_rate,omitempty"`
	InvolvesSalesTax    *bool    `json:"involves_sales_tax,omitempty"`
	IsInterimUKVAT      *bool    `json:"is_interim_uk_vat,omitempty"`
	RefundedOn          Date     `json:"refunded_on,omitzero"`
	WrittenOffDate      Date     `json:"written_off_date,omitzero"`
	CreatedAt           Time     `json:"created_at,omitzero"`
	UpdatedAt           Time     `json:"updated_at,omitzero"`
}

CreditNote is a credit issued against a contact, the mirror of an invoice.

See https://dev.freeagent.com/docs/credit_notes

type CreditNoteItem

type CreditNoteItem struct {
	URL ResourceURL `json:"url,omitempty"`
	// ID identifies an existing line on a write; leave unset to add one.
	ID *int64 `json:"id,omitempty"`
	// Destroy set to 1 removes the line on a write.
	Destroy *int `json:"_destroy,omitempty"`

	Position    *Decimal `json:"position,omitempty"`
	ItemType    string   `json:"item_type,omitempty"`
	Description string   `json:"description,omitempty"`
	Quantity    *Decimal `json:"quantity,omitempty"`
	Price       *Decimal `json:"price,omitempty"`

	SalesTaxRate         *Decimal `json:"sales_tax_rate,omitempty"`
	SalesTaxStatus       string   `json:"sales_tax_status,omitempty"`
	SecondSalesTaxRate   *Decimal `json:"second_sales_tax_rate,omitempty"`
	SecondSalesTaxStatus string   `json:"second_sales_tax_status,omitempty"`

	// Category is required.
	Category  ResourceURL `json:"category,omitempty"`
	Project   ResourceURL `json:"project,omitempty"`
	StockItem ResourceURL `json:"stock_item,omitempty"`
}

CreditNoteItem is one line on a credit note.

type CreditNoteReconciliation

type CreditNoteReconciliation struct {
	URL ResourceURL `json:"url,omitempty"`

	// All four are required.
	CreditNote ResourceURL `json:"credit_note,omitempty"`
	Invoice    ResourceURL `json:"invoice,omitempty"`
	GrossValue *Decimal    `json:"gross_value,omitempty"`
	DatedOn    Date        `json:"dated_on,omitzero"`

	Currency     string   `json:"currency,omitempty"`
	ExchangeRate *Decimal `json:"exchange_rate,omitempty"`

	// Read-only.
	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

CreditNoteReconciliation records how much of a credit note was applied to an invoice.

See https://dev.freeagent.com/docs/credit_note_reconciliations

type CreditNoteReconciliationService

type CreditNoteReconciliationService struct {
	Collection[CreditNoteReconciliation]
}

CreditNoteReconciliationService covers https://dev.freeagent.com/docs/credit_note_reconciliations

type CreditNoteService

type CreditNoteService struct {
	Collection[CreditNote]
}

CreditNoteService covers https://dev.freeagent.com/docs/credit_notes

func (*CreditNoteService) MarkAsDraft

func (s *CreditNoteService) MarkAsDraft(ctx context.Context, id int64) (*CreditNote, *Response, error)

MarkAsDraft returns the credit note to draft.

func (*CreditNoteService) MarkAsSent

func (s *CreditNoteService) MarkAsSent(ctx context.Context, id int64) (*CreditNote, *Response, error)

MarkAsSent moves a draft credit note to sent, or reopens a cancelled one.

func (*CreditNoteService) PDF

func (s *CreditNoteService) PDF(ctx context.Context, id int64) (*PDF, *Response, error)

PDF fetches the rendered credit note.

func (*CreditNoteService) SendEmail

func (s *CreditNoteService) SendEmail(ctx context.Context, id int64, opts *EmailOptions) (*Response, error)

SendEmail emails the credit note. Pass nil to use the account's template.

type Date

type Date struct {
	time.Time
}

Date is a calendar date with no time component, serialised as YYYY-MM-DD. The zero value marshals to null, which FreeAgent reads as unset.

func DateOf

func DateOf(t time.Time) Date

DateOf truncates t to its calendar date in t's own location.

func NewDate

func NewDate(year int, month time.Month, day int) Date

NewDate builds a Date in UTC.

func ParseDate

func ParseDate(s string) (Date, error)

ParseDate accepts the documented YYYY-MM-DD form and, tolerantly, a full timestamp: several endpoints document a date but return a timestamp.

func (Date) MarshalJSON

func (d Date) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (Date) String

func (d Date) String() string

String renders the date in wire format, or "" when zero.

func (*Date) UnmarshalJSON

func (d *Date) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler.

type Decimal

type Decimal = decimal.Decimal

Decimal carries money, rates and quantities. FreeAgent sends these as JSON strings such as "-90.0" and "0.25", so float64 would lose exactness on values the accounting system treats as authoritative.

type DepreciationProfile

type DepreciationProfile struct {
	Method                       string `json:"method,omitempty"`
	AssetLifeYears               *int   `json:"asset_life_years,omitempty"`
	AnnualDepreciationPercentage *int   `json:"annual_depreciation_percentage,omitempty"`
	// Frequency is monthly (the default) or annually.
	Frequency string `json:"frequency,omitempty"`
}

DepreciationProfile describes how an asset loses value.

It is not a resource of its own despite having a documentation page: there is no /v2/depreciation_profiles endpoint. It appears nested on a capital asset, and is accepted nested on expenses, bill items and bank transaction explanations when those create an asset.

AssetLifeYears applies to straight_line, AnnualDepreciationPercentage to reducing_balance; which one is required depends on Method.

See https://dev.freeagent.com/docs/depreciation_profiles

type ECMossRate

type ECMossRate struct {
	Percentage *Decimal `json:"percentage,omitempty"`
	// Band is Standard, Reduced, Parking or Super Reduced.
	Band string `json:"band,omitempty"`
}

ECMossRate is one VAT rate available for an EU country on a given date.

type ECMossRates

type ECMossRates struct {
	Rates []ECMossRate `json:"sales_tax_rates,omitempty"`
	// ECTaxName is the local name for the tax, usually VAT.
	ECTaxName string `json:"ec_tax_name,omitempty"`
}

ECMossRates is the reply from the EC VAT MOSS rate lookup.

type EmailAddressService

type EmailAddressService struct {
	// contains filtered or unexported fields
}

EmailAddressService covers https://dev.freeagent.com/docs/email_addresses

The reply is an array of plain strings, not of objects, each formatted as `Name <address@example.com>`. There is no model type for that reason.

func (*EmailAddressService) List

List returns the verified sender addresses for the company.

func (*EmailAddressService) Meta

Meta returns the resource metadata.

type EmailOptions

type EmailOptions struct {
	To          string `json:"to,omitempty"`
	From        string `json:"from,omitempty"`
	Subject     string `json:"subject,omitempty"`
	Body        string `json:"body,omitempty"`
	EmailToSelf bool   `json:"email_to_self,omitempty"`
}

EmailOptions overrides the message used when sending a document. A zero value tells FreeAgent to use the account's configured template.

type Environment

type Environment struct {
	Name         string
	BaseURL      string
	AuthorizeURL string
	TokenURL     string
}

Environment bundles the endpoints for one FreeAgent deployment.

One app registered at dev.freeagent.com serves both, so the client ID and secret are shared. What differs is the user account you approve with, and therefore the token, which is why tokens are stored under separate keys.

func EnvironmentByName

func EnvironmentByName(name string) (Environment, error)

EnvironmentByName resolves "production" or "sandbox".

func (Environment) OAuthConfig

func (e Environment) OAuthConfig(clientID, clientSecret, redirectURL string) *oauth2.Config

OAuthConfig builds the OAuth configuration for an environment. The token endpoint authenticates the app with HTTP Basic, so the auth style is pinned rather than probed.

type Estimate

type Estimate struct {
	URL ResourceURL `json:"url,omitempty"`

	Contact ResourceURL `json:"contact,omitempty"`
	Project ResourceURL `json:"project,omitempty"`

	// EstimateType is Estimate, Quote or Proposal.
	EstimateType string `json:"estimate_type,omitempty"`
	// Status is Draft, Sent, Open, Approved, Rejected or Invoiced.
	//
	// Unlike an invoice, an estimate will not accept a create without one:
	// omitting it returns 422 "status is not valid". Send "Draft" and use the
	// transitions from there.
	Status string `json:"status,omitempty"`

	Reference string `json:"reference,omitempty"`
	DatedOn   Date   `json:"dated_on,omitzero"`
	Currency  string `json:"currency,omitempty"`
	Notes     string `json:"notes,omitempty"`

	DiscountPercent   *Decimal `json:"discount_percent,omitempty"`
	ClientContactName string   `json:"client_contact_name,omitempty"`
	// ECStatus is UK/Non-EC, EC Goods, EC Services, Reverse Charge or
	// EC VAT MOSS.
	ECStatus      string `json:"ec_status,omitempty"`
	PlaceOfSupply string `json:"place_of_supply,omitempty"`

	IncludeSalesTaxOnTotalValue *bool `json:"include_sales_tax_on_total_value,omitempty"`

	EstimateItems []EstimateItem `json:"estimate_items,omitempty"`

	// Read-only.
	NetValue      *Decimal `json:"net_value,omitempty"`
	SalesTaxValue *Decimal `json:"sales_tax_value,omitempty"`
	CreatedAt     Time     `json:"created_at,omitzero"`
	UpdatedAt     Time     `json:"updated_at,omitzero"`
}

Estimate is a quote, proposal or estimate sent to a contact.

See https://dev.freeagent.com/docs/estimates

type EstimateItem

type EstimateItem struct {
	URL ResourceURL `json:"url,omitempty"`

	// Position starts at 1.
	Position *int `json:"position,omitempty"`
	// ItemType is Hours, Days, Weeks, Months, Years, -no unit-, Products,
	// Services, Training, Expenses, Comments, Bills, Discount or Credit.
	ItemType    string   `json:"item_type,omitempty"`
	Description string   `json:"description,omitempty"`
	Quantity    *Decimal `json:"quantity,omitempty"`
	Price       *Decimal `json:"price,omitempty"`

	SalesTaxRate         *Decimal `json:"sales_tax_rate,omitempty"`
	SalesTaxStatus       string   `json:"sales_tax_status,omitempty"`
	SalesTaxValue        *Decimal `json:"sales_tax_value,omitempty"`
	SecondSalesTaxRate   *Decimal `json:"second_sales_tax_rate,omitempty"`
	SecondSalesTaxStatus string   `json:"second_sales_tax_status,omitempty"`
	SecondSalesTaxValue  *Decimal `json:"second_sales_tax_value,omitempty"`

	Category ResourceURL `json:"category,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

EstimateItem is one line on an estimate.

type EstimateService

type EstimateService struct {
	Collection[Estimate]
}

EstimateService covers https://dev.freeagent.com/docs/estimates

func (*EstimateService) ConvertToInvoice

func (s *EstimateService) ConvertToInvoice(ctx context.Context, id int64) (*Estimate, *Response, error)

ConvertToInvoice turns the estimate into an invoice. The reply is the estimate as the API returns it, not the new invoice.

func (*EstimateService) Duplicate

func (s *EstimateService) Duplicate(ctx context.Context, id int64) (*Estimate, *Response, error)

Duplicate copies an existing estimate into a new draft.

func (*EstimateService) MarkAsApproved

func (s *EstimateService) MarkAsApproved(ctx context.Context, id int64) (*Estimate, *Response, error)

MarkAsApproved marks the estimate approved.

func (*EstimateService) MarkAsDraft

func (s *EstimateService) MarkAsDraft(ctx context.Context, id int64) (*Estimate, *Response, error)

MarkAsDraft returns the estimate to draft.

func (*EstimateService) MarkAsRejected

func (s *EstimateService) MarkAsRejected(ctx context.Context, id int64) (*Estimate, *Response, error)

MarkAsRejected marks the estimate rejected.

func (*EstimateService) MarkAsSent

func (s *EstimateService) MarkAsSent(ctx context.Context, id int64) (*Estimate, *Response, error)

MarkAsSent marks the estimate sent.

func (*EstimateService) PDF

func (s *EstimateService) PDF(ctx context.Context, id int64) (*PDF, *Response, error)

PDF fetches the rendered estimate.

func (*EstimateService) SendEmail

func (s *EstimateService) SendEmail(ctx context.Context, id int64, opts *EmailOptions) (*Response, error)

SendEmail emails the estimate. Pass nil to use the account's template.

type Expense

type Expense struct {
	URL ResourceURL `json:"url,omitempty"`

	// User and Category are required.
	User     ResourceURL `json:"user,omitempty"`
	Category ResourceURL `json:"category,omitempty"`
	Project  ResourceURL `json:"project,omitempty"`
	Property ResourceURL `json:"property,omitempty"`

	DatedOn          Date   `json:"dated_on,omitzero"`
	Currency         string `json:"currency,omitempty"`
	Description      string `json:"description,omitempty"`
	ReceiptReference string `json:"receipt_reference,omitempty"`

	GrossValue           *Decimal `json:"gross_value,omitempty"`
	NativeGrossValue     *Decimal `json:"native_gross_value,omitempty"`
	SalesTaxRate         *Decimal `json:"sales_tax_rate,omitempty"`
	SalesTaxValue        *Decimal `json:"sales_tax_value,omitempty"`
	NativeSalesTaxValue  *Decimal `json:"native_sales_tax_value,omitempty"`
	ManualSalesTaxAmount *Decimal `json:"manual_sales_tax_amount,omitempty"`
	// SalesTaxStatus is TAXABLE, EXEMPT or OUT_OF_SCOPE.
	SalesTaxStatus       string   `json:"sales_tax_status,omitempty"`
	SecondSalesTaxRate   *Decimal `json:"second_sales_tax_rate,omitempty"`
	SecondSalesTaxStatus string   `json:"second_sales_tax_status,omitempty"`
	// ECStatus is UK/Non-EC, EC Goods, EC Services or Reverse Charge.
	ECStatus string `json:"ec_status,omitempty"`

	// Rebilling. RebillFactor is required when RebillType is markup or price.
	RebillType      string      `json:"rebill_type,omitempty"`
	RebillFactor    *Decimal    `json:"rebill_factor,omitempty"`
	RebillToProject ResourceURL `json:"rebill_to_project,omitempty"`

	// Stock. Both are required for the purchase of stock category.
	StockItem             ResourceURL `json:"stock_item,omitempty"`
	StockAlteringQuantity *Decimal    `json:"stock_altering_quantity,omitempty"`

	// Mileage claims. VehicleType is Car, Motorcycle or Bicycle.
	Mileage            *Decimal `json:"mileage,omitempty"`
	VehicleType        string   `json:"vehicle_type,omitempty"`
	EngineType         string   `json:"engine_type,omitempty"`
	EngineSize         string   `json:"engine_size,omitempty"`
	ReclaimMileage     *int     `json:"reclaim_mileage,omitempty"`
	InitialRateMileage *Decimal `json:"initial_rate_mileage,omitempty"`
	ReclaimMileageRate *Decimal `json:"reclaim_mileage_rate,omitempty"`
	RebillMileageRate  *Decimal `json:"rebill_mileage_rate,omitempty"`
	HaveVATReceipt     *bool    `json:"have_vat_receipt,omitempty"`

	// Recurring is Weekly, Two Weekly, Four Weekly, Two Monthly, Quarterly,
	// Biannually, Annually or 2-Yearly.
	Recurring        string `json:"recurring,omitempty"`
	NextRecursOn     Date   `json:"next_recurs_on,omitzero"`
	RecurringEndDate Date   `json:"recurring_end_date,omitzero"`

	Attachment *Attachment `json:"attachment,omitempty"`

	// Read-only.
	CapitalAsset         ResourceURL `json:"capital_asset,omitempty"`
	RebilledOnInvoice    ResourceURL `json:"rebilled_on_invoice,omitempty"`
	StockItemDescription string      `json:"stock_item_description,omitempty"`
	CreatedAt            Time        `json:"created_at,omitzero"`
	UpdatedAt            Time        `json:"updated_at,omitzero"`
}

Expense is an out-of-pocket cost incurred by a user.

GrossValue is negative for a payment and positive for a refund. It is required unless the category is Mileage, in which case Mileage and VehicleType are.

See https://dev.freeagent.com/docs/expenses

type ExpenseService

type ExpenseService struct {
	Collection[Expense]
}

ExpenseService covers https://dev.freeagent.com/docs/expenses

func (*ExpenseService) MileageSettings

func (s *ExpenseService) MileageSettings(ctx context.Context) (*MileageSettings, *Response, error)

MileageSettings returns the account's mileage configuration. The reply is enveloped under mileage_settings, unlike the other expense sub-resources.

type FieldError

type FieldError struct {
	Field   string
	Message string
}

FieldError is one validation failure attributed to a specific attribute. Field is empty when the API reported the failure without naming one.

func (FieldError) String

func (f FieldError) String() string

type FileStore

type FileStore struct {
	// contains filtered or unexported fields
}

FileStore persists tokens as 0600 JSON. One file holds one entry per environment, so sandbox and production credentials coexist without the caller juggling paths.

func NewFileStore

func NewFileStore(path, key string) (*FileStore, error)

NewFileStore stores tokens for one environment at path. Pass the environment name as key.

func (*FileStore) Load

func (f *FileStore) Load(context.Context) (*oauth2.Token, error)

Load implements TokenStore.

func (*FileStore) Path

func (f *FileStore) Path() string

Path returns the file backing the store.

func (*FileStore) Save

func (f *FileStore) Save(_ context.Context, token *oauth2.Token) error

Save implements TokenStore. The write is atomic so an interrupted save cannot leave a truncated credential file behind.

type FilingPayment

type FilingPayment struct {
	Label     string   `json:"label,omitempty"`
	DueOn     Date     `json:"due_on,omitzero"`
	AmountDue *Decimal `json:"amount_due,omitempty"`
	// Status is unpaid or marked_as_paid, and is omitted entirely when the
	// amount due is zero or negative.
	Status string `json:"status,omitempty"`
}

FilingPayment is one payment due against a filing period.

type FinalAccountsReport

type FinalAccountsReport struct {
	// URL ends in the period end date rather than a numeric id, so
	// ResourceURL.ID does not apply to it. Use PeriodEndsOn.
	URL ResourceURL `json:"url,omitempty"`

	PeriodStartsOn Date `json:"period_starts_on,omitzero"`
	PeriodEndsOn   Date `json:"period_ends_on,omitzero"`
	FilingDueOn    Date `json:"filing_due_on,omitzero"`

	// FilingStatus is draft, unfiled, pending, rejected, filed or
	// marked_as_filed.
	FilingStatus   string `json:"filing_status,omitempty"`
	FiledAt        Time   `json:"filed_at,omitzero"`
	FiledReference string `json:"filed_reference,omitempty"`
}

FinalAccountsReport is one accounting period's end-of-year filing state.

See https://dev.freeagent.com/docs/final_accounts_reports

type FinalAccountsReportService

type FinalAccountsReportService struct {
	// contains filtered or unexported fields
}

FinalAccountsReportService covers https://dev.freeagent.com/docs/final_accounts_reports

Reports are addressed by the period end date, not a numeric id, which is the shape it shares with the VAT, corporation tax and income tax families.

func (*FinalAccountsReportService) Get

Get fetches one period's report by its end date.

func (*FinalAccountsReportService) List

List returns every accounting period's report.

func (*FinalAccountsReportService) MarkAsFiled

func (s *FinalAccountsReportService) MarkAsFiled(ctx context.Context, periodEndsOn Date) (*FinalAccountsReport, *Response, error)

MarkAsFiled records the period as filed outside FreeAgent. It needs Full Access, or Account Manager on a practice-managed account.

func (*FinalAccountsReportService) MarkAsUnfiled

func (s *FinalAccountsReportService) MarkAsUnfiled(ctx context.Context, periodEndsOn Date) (*FinalAccountsReport, *Response, error)

MarkAsUnfiled reverses MarkAsFiled.

func (*FinalAccountsReportService) Meta

func (s *FinalAccountsReportService) Meta() ResourceMeta

Meta returns the resource metadata.

type HirePurchase

type HirePurchase struct {
	URL ResourceURL `json:"url,omitempty"`

	Description string      `json:"description,omitempty"`
	Bill        ResourceURL `json:"bill,omitempty"`

	LiabilitiesOverOneYearCategory  ResourceURL `json:"liabilities_over_one_year_category,omitempty"`
	LiabilitiesUnderOneYearCategory ResourceURL `json:"liabilities_under_one_year_category,omitempty"`
}

HirePurchase is a bill paid off in instalments.

Every field is read-only: the record is created by flagging a bill as paid by hire purchase, not through this endpoint. UK companies only.

See https://dev.freeagent.com/docs/hire_purchases

type HirePurchaseService

type HirePurchaseService struct {
	ReadCollection[HirePurchase]
}

HirePurchaseService covers https://dev.freeagent.com/docs/hire_purchases

type IncomeTaxReturn

type IncomeTaxReturn struct {
	URL ResourceURL `json:"url,omitempty"`

	PeriodStartsOn Date `json:"period_starts_on,omitzero"`
	PeriodEndsOn   Date `json:"period_ends_on,omitzero"`
	FilingDueOn    Date `json:"filing_due_on,omitzero"`

	// FilingStatus is unfiled, pending, rejected, provisionally_filed, filed
	// or marked_as_filed. provisionally_filed is unique to this family.
	FilingStatus   string `json:"filing_status,omitempty"`
	FiledAt        Time   `json:"filed_at,omitzero"`
	FiledReference string `json:"filed_reference,omitempty"`

	Payments []FilingPayment `json:"payments,omitempty"`
}

IncomeTaxReturn is one self assessment period for a user.

The documentation has two pages for this, Self Assessment Returns and Income Tax Returns; the former redirects to the latter and they are the same resource. Neither /v2/self_assessment_returns nor /v2/income_tax_returns exists: the collection is nested under a user.

See https://dev.freeagent.com/docs/income_tax_returns

type IncomeTaxReturnService

type IncomeTaxReturnService struct {
	// contains filtered or unexported fields
}

IncomeTaxReturnService covers https://dev.freeagent.com/docs/income_tax_returns

Every call is scoped to a user, so each method takes the user's URL.

func (*IncomeTaxReturnService) GetForUser

func (s *IncomeTaxReturnService) GetForUser(ctx context.Context, user ResourceURL, periodEndsOn Date) (*IncomeTaxReturn, *Response, error)

GetForUser fetches one period for a user.

func (*IncomeTaxReturnService) ListForUser

ListForUser returns every self assessment period for a user.

func (*IncomeTaxReturnService) MarkAsFiled

func (s *IncomeTaxReturnService) MarkAsFiled(ctx context.Context, user ResourceURL, periodEndsOn Date) (*IncomeTaxReturn, *Response, error)

MarkAsFiled records a user's return as filed. Needs Full Access.

func (*IncomeTaxReturnService) MarkAsUnfiled

func (s *IncomeTaxReturnService) MarkAsUnfiled(ctx context.Context, user ResourceURL, periodEndsOn Date) (*IncomeTaxReturn, *Response, error)

MarkAsUnfiled reverses MarkAsFiled.

func (*IncomeTaxReturnService) MarkPaymentAsPaid

func (s *IncomeTaxReturnService) MarkPaymentAsPaid(ctx context.Context, user ResourceURL, periodEndsOn, paymentDate Date) (*IncomeTaxReturn, *Response, error)

MarkPaymentAsPaid records one dated payment as settled.

func (*IncomeTaxReturnService) MarkPaymentAsUnpaid

func (s *IncomeTaxReturnService) MarkPaymentAsUnpaid(ctx context.Context, user ResourceURL, periodEndsOn, paymentDate Date) (*IncomeTaxReturn, *Response, error)

MarkPaymentAsUnpaid reverses MarkPaymentAsPaid.

func (*IncomeTaxReturnService) Meta

func (s *IncomeTaxReturnService) Meta() ResourceMeta

Meta returns the resource metadata.

type Int64

type Int64 int64

Int64 accepts either a JSON number or a numeric string, and always writes a number. FreeAgent is inconsistent here: the company documentation types id as an integer while the example on the same page returns "12345" quoted.

func Int64Of

func Int64Of(v int64) Int64

Int64Of converts a plain int64.

func (Int64) MarshalJSON

func (n Int64) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (*Int64) UnmarshalJSON

func (n *Int64) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (Int64) Value

func (n Int64) Value() int64

Value returns the underlying integer.

type Invoice

type Invoice struct {
	URL ResourceURL `json:"url,omitempty"`

	// Contact is required.
	Contact  ResourceURL `json:"contact,omitempty"`
	Project  ResourceURL `json:"project,omitempty"`
	Property ResourceURL `json:"property,omitempty"`
	// BankAccount nominates the account shown for remittance.
	BankAccount ResourceURL `json:"bank_account,omitempty"`

	// Reference is required; omit it to use the account's invoice sequence.
	Reference string `json:"reference,omitempty"`
	// DatedOn is required.
	DatedOn Date `json:"dated_on,omitzero"`
	DueOn   Date `json:"due_on,omitzero"`
	// PaymentTermsInDays is required; zero means due on receipt.
	PaymentTermsInDays *int   `json:"payment_terms_in_days,omitempty"`
	Currency           string `json:"currency,omitempty"`

	// Status is one of Draft, Scheduled To Email, Open, Zero Value, Overdue,
	// Paid, Overpaid, Refunded, Written-off or Part written-off. It is driven
	// by the transitions rather than set directly.
	Status string `json:"status,omitempty"`

	Comments             string   `json:"comments,omitempty"`
	DiscountPercent      *Decimal `json:"discount_percent,omitempty"`
	ClientContactName    string   `json:"client_contact_name,omitempty"`
	PaymentTerms         string   `json:"payment_terms,omitempty"`
	POReference          string   `json:"po_reference,omitempty"`
	OmitHeader           *bool    `json:"omit_header,omitempty"`
	ShowProjectName      *bool    `json:"show_project_name,omitempty"`
	AlwaysShowBICAndIBAN *bool    `json:"always_show_bic_and_iban,omitempty"`

	SendNewInvoiceEmails *bool `json:"send_new_invoice_emails,omitempty"`
	SendReminderEmails   *bool `json:"send_reminder_emails,omitempty"`
	SendThankYouEmails   *bool `json:"send_thank_you_emails,omitempty"`

	// Roll-up options. Each is either null or one of the documented
	// billed_grouped_by_* values.
	IncludeTimeslips string `json:"include_timeslips,omitempty"`
	IncludeExpenses  string `json:"include_expenses,omitempty"`
	IncludeEstimates string `json:"include_estimates,omitempty"`

	// ECStatus is UK/Non-EC, EC Goods, EC Services, Reverse Charge or
	// EC VAT MOSS.
	ECStatus      string `json:"ec_status,omitempty"`
	PlaceOfSupply string `json:"place_of_supply,omitempty"`

	// Construction Industry Scheme.
	CISRate              string   `json:"cis_rate,omitempty"`
	CISDeductionRate     *Decimal `json:"cis_deduction_rate,omitempty"`
	CISDeduction         *Decimal `json:"cis_deduction,omitempty"`
	CISDeductionSuffered *Decimal `json:"cis_deduction_suffered,omitempty"`

	InvoiceItems []InvoiceItem `json:"invoice_items,omitempty"`

	// Read-only.
	LongStatus          string                 `json:"long_status,omitempty"`
	ContactName         string                 `json:"contact_name,omitempty"`
	NetValue            *Decimal               `json:"net_value,omitempty"`
	SalesTaxValue       *Decimal               `json:"sales_tax_value,omitempty"`
	SecondSalesTaxValue *Decimal               `json:"second_sales_tax_value,omitempty"`
	TotalValue          *Decimal               `json:"total_value,omitempty"`
	PaidValue           *Decimal               `json:"paid_value,omitempty"`
	DueValue            *Decimal               `json:"due_value,omitempty"`
	ExchangeRate        *Decimal               `json:"exchange_rate,omitempty"`
	InvolvesSalesTax    *bool                  `json:"involves_sales_tax,omitempty"`
	IsInterimUKVAT      *bool                  `json:"is_interim_uk_vat,omitempty"`
	PaidOn              Date                   `json:"paid_on,omitzero"`
	WrittenOffDate      Date                   `json:"written_off_date,omitzero"`
	RecurringInvoice    ResourceURL            `json:"recurring_invoice,omitempty"`
	PaymentURL          string                 `json:"payment_url,omitempty"`
	PaymentMethods      *InvoicePaymentMethods `json:"payment_methods,omitempty"`
	CreatedAt           Time                   `json:"created_at,omitzero"`
	UpdatedAt           Time                   `json:"updated_at,omitzero"`
}

Invoice is a sales invoice.

See https://dev.freeagent.com/docs/invoices

type InvoiceItem

type InvoiceItem struct {
	URL ResourceURL `json:"url,omitempty"`
	// ID identifies an existing line on a write. Leave it unset to add one.
	ID *int64 `json:"id,omitempty"`
	// Destroy set to 1 removes the line on a write.
	Destroy *int `json:"_destroy,omitempty"`

	// Position is read-only and starts at 1.
	Position *Decimal `json:"position,omitempty"`
	// ItemType is Hours, Days, Weeks, Months, Years, Products, Services,
	// Training, Expenses, Comment, Bills, Discount, Credit, VAT or Stock.
	// Blank means "no unit".
	ItemType    string   `json:"item_type,omitempty"`
	Description string   `json:"description,omitempty"`
	Quantity    *Decimal `json:"quantity,omitempty"`
	Price       *Decimal `json:"price,omitempty"`

	SalesTaxRate         *Decimal `json:"sales_tax_rate,omitempty"`
	SalesTaxStatus       string   `json:"sales_tax_status,omitempty"`
	SecondSalesTaxRate   *Decimal `json:"second_sales_tax_rate,omitempty"`
	SecondSalesTaxStatus string   `json:"second_sales_tax_status,omitempty"`

	Category  ResourceURL `json:"category,omitempty"`
	Project   ResourceURL `json:"project,omitempty"`
	StockItem ResourceURL `json:"stock_item,omitempty"`
}

InvoiceItem is one line on an invoice.

type InvoicePaymentMethods

type InvoicePaymentMethods struct {
	PayPal                   bool `json:"paypal,omitempty"`
	GoCardlessPreauth        bool `json:"gocardless_preauth,omitempty"`
	GoCardlessInstantBankPay bool `json:"gocardless_instant_bank_pay,omitempty"`
	Stripe                   bool `json:"stripe,omitempty"`
	Tyl                      bool `json:"tyl,omitempty"`
}

InvoicePaymentMethods reports which online payment routes are enabled. All fields are read-only and depend on the integrations configured.

type InvoiceService

type InvoiceService struct {
	Collection[Invoice]
}

InvoiceService covers https://dev.freeagent.com/docs/invoices

func (*InvoiceService) ConvertToCreditNote

func (s *InvoiceService) ConvertToCreditNote(ctx context.Context, id int64) (*Invoice, *Response, error)

ConvertToCreditNote turns a draft invoice with a negative total into a credit note.

func (*InvoiceService) Duplicate

func (s *InvoiceService) Duplicate(ctx context.Context, id int64) (*Invoice, *Response, error)

Duplicate copies an existing invoice into a new draft.

func (*InvoiceService) MarkAsCancelled

func (s *InvoiceService) MarkAsCancelled(ctx context.Context, id int64) (*Invoice, *Response, error)

MarkAsCancelled cancels the invoice.

func (*InvoiceService) MarkAsDraft

func (s *InvoiceService) MarkAsDraft(ctx context.Context, id int64) (*Invoice, *Response, error)

MarkAsDraft returns the invoice to draft.

func (*InvoiceService) MarkAsScheduled

func (s *InvoiceService) MarkAsScheduled(ctx context.Context, id int64) (*Invoice, *Response, error)

MarkAsScheduled schedules the invoice to be emailed.

func (*InvoiceService) MarkAsSent

func (s *InvoiceService) MarkAsSent(ctx context.Context, id int64) (*Invoice, *Response, error)

MarkAsSent moves a draft invoice to sent, or reopens a cancelled one.

func (*InvoiceService) PDF

func (s *InvoiceService) PDF(ctx context.Context, id int64) (*PDF, *Response, error)

PDF fetches the rendered invoice.

func (*InvoiceService) SendEmail

func (s *InvoiceService) SendEmail(ctx context.Context, id int64, opts *EmailOptions) (*Response, error)

SendEmail emails the invoice. Pass nil to use the account's template.

type JournalEntry

type JournalEntry struct {
	URL ResourceURL `json:"url,omitempty"`
	// Destroy removes the entry on an update.
	Destroy *bool `json:"_destroy,omitempty"`

	// Category and DebitValue are required.
	Category    ResourceURL `json:"category,omitempty"`
	DebitValue  *Decimal    `json:"debit_value,omitempty"`
	Description string      `json:"description,omitempty"`

	// Each of these applies only to the matching category range.
	CapitalAssetType      ResourceURL `json:"capital_asset_type,omitempty"`
	User                  ResourceURL `json:"user,omitempty"`
	StockItem             ResourceURL `json:"stock_item,omitempty"`
	StockAlteringQuantity *int        `json:"stock_altering_quantity,omitempty"`
	BankAccount           ResourceURL `json:"bank_account,omitempty"`
	Property              ResourceURL `json:"property,omitempty"`
	Contact               ResourceURL `json:"contact,omitempty"`
}

JournalEntry is one side of a journal set. A negative DebitValue is a credit, and a set must balance to zero.

type JournalSet

type JournalSet struct {
	URL ResourceURL `json:"url,omitempty"`

	// DatedOn and Description are required, except on the opening balances
	// set which has no date.
	DatedOn     Date   `json:"dated_on,omitzero"`
	Description string `json:"description,omitempty"`
	// Tag marks sets created by your integration. Tagged sets are read-only
	// in the FreeAgent interface, which stops a user editing them out from
	// under you.
	Tag string `json:"tag,omitempty"`

	JournalEntries []JournalEntry `json:"journal_entries,omitempty"`

	// Read-only, and present on the opening balances set only. The
	// documentation types both as bare arrays; they are arrays of objects
	// carrying their own value, not lists of references.
	BankAccounts []JournalSetBalance `json:"bank_accounts,omitempty"`
	StockItems   []JournalSetBalance `json:"stock_items,omitempty"`

	UpdatedAt Time `json:"updated_at,omitzero"`
}

JournalSet is a balanced set of double-entry journal entries.

See https://dev.freeagent.com/docs/journal_sets

type JournalSetBalance

type JournalSetBalance struct {
	URL         ResourceURL `json:"url,omitempty"`
	Description string      `json:"description,omitempty"`
	DebitValue  *Decimal    `json:"debit_value,omitempty"`
}

JournalSetBalance is one opening-balance line for a bank account or a stock item, as carried on the opening balances journal set.

type JournalSetService

type JournalSetService struct {
	Collection[JournalSet]
}

JournalSetService covers https://dev.freeagent.com/docs/journal_sets

func (*JournalSetService) OpeningBalances

func (s *JournalSetService) OpeningBalances(ctx context.Context) (*JournalSet, *Response, error)

OpeningBalances returns the account's opening balance journal set, which has its own endpoint rather than an id.

It is not an ordinary journal set. It carries no date, and its bank and stock legs live in BankAccounts and StockItems rather than in JournalEntries, so the entries alone do not sum to zero.

type ListOptions

type ListOptions struct {
	// Page is 1-based. Zero means the server default (page 1).
	Page int
	// PerPage defaults to 25 server-side and may not exceed MaxPerPage.
	PerPage int
	// UpdatedSince filters to records changed at or after this instant. It is
	// the basis for incremental reads.
	UpdatedSince Time
	// Sort is a field name, optionally prefixed with "-" for descending, for
	// example "-updated_at".
	Sort string
	// View selects a server-side named filter such as "open_or_overdue".
	View string
	// FromDate and ToDate bound date-ranged collections such as bank
	// transactions.
	FromDate Date
	ToDate   Date
	// Extra is merged last and wins on key collisions.
	Extra url.Values
}

ListOptions carries the filters shared by the collection endpoints. Not every resource honours every field; the API ignores what it does not know, and Extra is the escape hatch for resource-specific parameters.

Example

ListOptions renders the filters an incremental read depends on.

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/alekc/freeagent-sdk/freeagent"
)

func main() {
	cursor := time.Date(2026, time.August, 1, 9, 30, 0, 0, time.UTC)
	opts := &freeagent.ListOptions{
		UpdatedSince: freeagent.TimeOf(cursor),
		Sort:         "updated_at",
		PerPage:      freeagent.MaxPerPage,
	}
	values, err := opts.Values()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(values.Encode())
}
Output:
per_page=100&sort=updated_at&updated_since=2026-08-01T09%3A30%3A00.000Z

func (*ListOptions) Values

func (o *ListOptions) Values() (url.Values, error)

Values renders the options as a query string, rejecting values the API would refuse rather than letting the request go out and fail remotely. It is exported so callers using Client.Raw get the same validation.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore keeps a token for the life of the process. Useful in tests and for short-lived jobs that receive a token by other means.

func NewMemoryStore

func NewMemoryStore(token *oauth2.Token) *MemoryStore

NewMemoryStore seeds a store, optionally with an existing token.

func (*MemoryStore) Load

Load implements TokenStore.

func (*MemoryStore) Save

func (m *MemoryStore) Save(_ context.Context, token *oauth2.Token) error

Save implements TokenStore.

type MileagePeriod

type MileagePeriod[T any] struct {
	From  Date `json:"from,omitzero"`
	To    Date `json:"to,omitzero"`
	Value T    `json:"value,omitempty"`
}

MileagePeriod is one dated slice of a mileage setting.

func (MileagePeriod[T]) RatesFor

func (p MileagePeriod[T]) RatesFor(vehicleType string) (MileageRate, bool)

RatesFor decodes the per-vehicle rates in a period, skipping the basic_rate_limit sibling that shares the object.

type MileageRate

type MileageRate struct {
	BasicRate      *Decimal `json:"basic_rate,omitempty"`
	AdditionalRate *Decimal `json:"additional_rate,omitempty"`
}

MileageRate is the rate pair for one vehicle type inside a MileageRates period. Decode a member of the period's Value into it; the sibling basic_rate_limit key is a number, not a rate, so it will not fit.

type MileageSettings

type MileageSettings struct {
	// EngineTypeAndSizeOptions maps an engine type such as "Petrol" to the
	// engine sizes valid in that period.
	EngineTypeAndSizeOptions []MileagePeriod[map[string][]string] `json:"engine_type_and_size_options,omitempty"`
	// MileageRates maps a vehicle type such as "Car" to its rates. The same
	// object also carries a basic_rate_limit, which is why the value is
	// decoded loosely rather than as a fixed struct.
	MileageRates []MileagePeriod[map[string]json.RawMessage] `json:"mileage_rates,omitempty"`
}

MileageSettings reports the account's mileage configuration.

Both members are historical: each entry covers a date range, because HMRC rates and the engine-size bands have changed over time. Pick the entry whose range contains the expense date rather than assuming the last one applies.

type Note

type Note struct {
	URL ResourceURL `json:"url,omitempty"`

	// Note is the content, and is required.
	Note string `json:"note,omitempty"`

	// Read-only. ParentURL points at the contact or project the note hangs
	// off; the parent is chosen with a query parameter on create, not by
	// setting this field.
	ParentURL ResourceURL `json:"parent_url,omitempty"`
	Author    string      `json:"author,omitempty"`
	CreatedAt Time        `json:"created_at,omitzero"`
	UpdatedAt Time        `json:"updated_at,omitzero"`
}

Note is a free-text note attached to a contact or a project.

See https://dev.freeagent.com/docs/notes

type NoteService

type NoteService struct {
	Collection[Note]
}

NoteService covers https://dev.freeagent.com/docs/notes

Notes are always scoped to a parent. Listing without one is a 400, and creating without one has nowhere to attach, so the inherited List, All and Create are shadowed in favour of the parent-taking forms.

func (*NoteService) All

All is unavailable without a parent. Use AllForParent.

func (*NoteService) AllForParent

func (s *NoteService) AllForParent(ctx context.Context, parent ResourceURL, opts *ListOptions) iter.Seq2[Note, error]

AllForParent iterates every note on one contact or project.

func (*NoteService) Create

func (s *NoteService) Create(context.Context, *Note) (*Note, *Response, error)

Create is unavailable without a parent. Use CreateForParent.

func (*NoteService) CreateForParent

func (s *NoteService) CreateForParent(ctx context.Context, parent ResourceURL, in *Note) (*Note, *Response, error)

CreateForParent adds a note to one contact or project.

func (*NoteService) List

List is unavailable without a parent. Use ListForParent.

func (*NoteService) ListForParent

func (s *NoteService) ListForParent(ctx context.Context, parent ResourceURL, opts *ListOptions) ([]Note, *Response, error)

ListForParent fetches the notes on one contact or project.

type Option

type Option func(*Client) error

Option configures a Client.

func WithAPIVersion

func WithAPIVersion(v string) Option

WithAPIVersion overrides the X-Api-Version header. The value is a date such as "2024-10-01"; the API serves the newest version at or before it.

func WithBaseURL

func WithBaseURL(raw string) Option

WithBaseURL overrides the API endpoint. A trailing slash is added when missing so relative resource paths resolve correctly.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient supplies the underlying transport.

func WithMaxResponseBytes

func WithMaxResponseBytes(n int64) Option

WithMaxResponseBytes overrides the response body cap.

func WithRateLimitTest

func WithRateLimitTest(on bool) Option

WithRateLimitTest sends X-RateLimit-Test, which lowers the sandbox budget to 5 requests per minute so back-off handling can be exercised for real.

func WithRateLimits

func WithRateLimits(perMinute, perHour int) Option

WithRateLimits sets the client-side request budgets. Zero disables the corresponding limiter.

func WithReadOnly

func WithReadOnly() Option

WithReadOnly refuses every mutating request before it is built, returning ErrReadOnly instead. Only GET, HEAD and OPTIONS are allowed through.

This exists for pointing a client at an account whose data must not be touched. It is a structural guarantee rather than a matter of discipline: no typed service, no transition, no Raw call can write through a read-only client, because the check sits in request construction rather than in each caller.

func WithRetryPolicy

func WithRetryPolicy(p RetryPolicy) Option

WithRetryPolicy overrides the retry behaviour.

func WithSandbox

func WithSandbox() Option

WithSandbox points the client at the sandbox environment.

func WithTokenSource

func WithTokenSource(ts oauth2.TokenSource) Option

WithTokenSource supplies OAuth credentials. Use TokenSource for a source that refreshes and persists rotated refresh tokens.

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent replaces the default user agent.

func WithoutAuth

func WithoutAuth() Option

WithoutAuth builds a client that sends no Authorization header. It exists for tests and for talking to a local fake; real endpoints will reject it.

func WithoutRateLimit

func WithoutRateLimit() Option

WithoutRateLimit removes client-side throttling. The server limits still apply, so the retry path becomes the only protection.

func WithoutRetry

func WithoutRetry() Option

WithoutRetry disables automatic retries.

type PDF

type PDF struct {
	Content string `json:"content"`
}

PDF is a rendered document. FreeAgent returns the file base64 encoded in a JSON envelope rather than as an octet stream.

func (*PDF) Bytes

func (p *PDF) Bytes() ([]byte, error)

Bytes decodes the document. The encoded form is kept on the struct so a caller can hand it straight back to something that wants base64.

type PayrollPeriod

type PayrollPeriod struct {
	URL ResourceURL `json:"url,omitempty"`

	Period    *int   `json:"period,omitempty"`
	Frequency string `json:"frequency,omitempty"`
	DatedOn   Date   `json:"dated_on,omitzero"`
	// Status is unfiled, pending, rejected, partially_filed or filed.
	Status string `json:"status,omitempty"`

	EmploymentAllowanceClaimed          *bool    `json:"employment_allowance_claimed,omitempty"`
	EmploymentAllowanceAmount           *Decimal `json:"employment_allowance_amount,omitempty"`
	ConstructionIndustrySchemeDeduction *Decimal `json:"construction_industry_scheme_deduction,omitempty"`

	// Payslips is populated when a single period is fetched, and absent from
	// the year listing.
	Payslips []Payslip `json:"payslips,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

PayrollPeriod is one pay run in a tax year. Every field is read-only: payroll is filed through the FreeAgent interface, not the API.

See https://dev.freeagent.com/docs/payroll

type PayrollProfile

type PayrollProfile struct {
	User ResourceURL `json:"user,omitempty"`

	PayrollReference string `json:"payroll_reference,omitempty"`
	Title            string `json:"title,omitempty"`
	Gender           string `json:"gender,omitempty"`
	DateOfBirth      Date   `json:"date_of_birth,omitzero"`

	// The address lines are numbered rather than named here, unlike the
	// address1..address3 used everywhere else in the API.
	AddressLine1 string `json:"address_line_1,omitempty"`
	AddressLine2 string `json:"address_line_2,omitempty"`
	AddressLine3 string `json:"address_line_3,omitempty"`
	AddressLine4 string `json:"address_line_4,omitempty"`
	Postcode     string `json:"postcode,omitempty"`
	Country      string `json:"country,omitempty"`

	TotalPayInPreviousEmployment *Decimal `json:"total_pay_in_previous_employment,omitempty"`
	TotalTaxInPreviousEmployment *Decimal `json:"total_tax_in_previous_employment,omitempty"`

	EmploymentStartsOn Date `json:"employment_starts_on,omitzero"`
	EmploymentEndsOn   Date `json:"employment_ends_on,omitzero"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

PayrollProfile is an employee's payroll details for a tax year.

See https://dev.freeagent.com/docs/payroll_profiles

type PayrollProfileService

type PayrollProfileService struct {
	// contains filtered or unexported fields
}

PayrollProfileService covers https://dev.freeagent.com/docs/payroll_profiles

Like payroll, addressed by tax year: /v2/payroll_profiles alone is a 404.

func (*PayrollProfileService) Meta

Meta returns the resource metadata.

func (*PayrollProfileService) Year

Year lists every profile for a tax year. Pass a user URL to narrow it to one employee, or the zero value for all of them.

type PayrollService

type PayrollService struct {
	// contains filtered or unexported fields
}

PayrollService covers https://dev.freeagent.com/docs/payroll

Payroll is addressed by tax year rather than by id, and there is no collection at /v2/payroll: the year is part of the path. UK companies with payroll only.

func (*PayrollService) MarkPaymentAsPaid

func (s *PayrollService) MarkPaymentAsPaid(ctx context.Context, year int, paymentDate Date) (*Response, error)

MarkPaymentAsPaid records an HMRC payment for the year as settled.

The documentation lists the unpaid variant as a GET, which is a typo: both are PUT, matching every other mark_as_* transition in the API.

func (*PayrollService) MarkPaymentAsUnpaid

func (s *PayrollService) MarkPaymentAsUnpaid(ctx context.Context, year int, paymentDate Date) (*Response, error)

MarkPaymentAsUnpaid reverses MarkPaymentAsPaid.

func (*PayrollService) Meta

func (s *PayrollService) Meta() ResourceMeta

Meta returns the resource metadata.

func (*PayrollService) Payslips

func (s *PayrollService) Payslips(ctx context.Context, year, period int) ([]Payslip, *Response, error)

Payslips returns just the payslips in one period.

func (*PayrollService) Period

func (s *PayrollService) Period(ctx context.Context, year, period int) (*PayrollPeriod, *Response, error)

Period fetches one period of a tax year, including its payslips.

The reply is enveloped under "period" and carries the payslips nested inside it, not under a top-level "payslips" key as the documentation's wording suggests.

func (*PayrollService) Year

func (s *PayrollService) Year(ctx context.Context, year int) (*PayrollYear, *Response, error)

Year lists the periods and HMRC payments for one tax year.

type PayrollYear

type PayrollYear struct {
	Periods  []PayrollPeriod `json:"periods,omitempty"`
	Payments []FilingPayment `json:"payments,omitempty"`
}

PayrollYear is what a year's payroll listing returns: its periods and the payments due to HMRC.

type Payslip

type Payslip struct {
	User    ResourceURL `json:"user,omitempty"`
	DatedOn Date        `json:"dated_on,omitzero"`

	TaxCode   string `json:"tax_code,omitempty"`
	NILetter  string `json:"ni_letter,omitempty"`
	Frequency string `json:"frequency,omitempty"`
	// NICalcType is the National Insurance calculation basis.
	NICalcType string `json:"ni_calc_type,omitempty"`

	BasicPay    *Decimal `json:"basic_pay,omitempty"`
	Overtime    *Decimal `json:"overtime,omitempty"`
	Commission  *Decimal `json:"commission,omitempty"`
	Bonus       *Decimal `json:"bonus,omitempty"`
	Allowance   *Decimal `json:"allowance,omitempty"`
	HoursWorked *Decimal `json:"hours_worked,omitempty"`

	TaxDeducted *Decimal `json:"tax_deducted,omitempty"`
	EmployeeNI  *Decimal `json:"employee_ni,omitempty"`
	EmployerNI  *Decimal `json:"employer_ni,omitempty"`

	// Statutory payments.
	StatutorySickPay                *Decimal `json:"statutory_sick_pay,omitempty"`
	StatutoryMaternityPay           *Decimal `json:"statutory_maternity_pay,omitempty"`
	StatutoryPaternityPay           *Decimal `json:"statutory_paternity_pay,omitempty"`
	AdditionalStatutoryPaternityPay *Decimal `json:"additional_statutory_paternity_pay,omitempty"`
	StatutoryAdoptionPay            *Decimal `json:"statutory_adoption_pay,omitempty"`
	StatutoryParentalBereavementPay *Decimal `json:"statutory_parental_bereavement_pay,omitempty"`
	// Undocumented, but sent by the live API: Northern Ireland has its own
	// parental bereavement figure.
	StatutoryParentalBereavementPayNIreland *Decimal `json:"statutory_parental_bereavement_pay_n_ireland,omitempty"`
	StatutoryNeonatalCarePay                *Decimal `json:"statutory_neonatal_care_pay,omitempty"`
	AbsencePayments                         *Decimal `json:"absence_payments,omitempty"`
	OtherPayments                           *Decimal `json:"other_payments,omitempty"`

	// Pensions and salary sacrifice.
	EmployeePension                *Decimal `json:"employee_pension,omitempty"`
	EmployerPension                *Decimal `json:"employer_pension,omitempty"`
	EmployeePensionNotUnderNetPay  *Decimal `json:"employee_pension_not_under_net_pay,omitempty"`
	EmployeePensionSalarySacrifice *Decimal `json:"employee_pension_salary_sacrifice,omitempty"`
	OtherSalarySacrificeDeductions *Decimal `json:"other_salary_sacrifice_deductions,omitempty"`

	// Deductions.
	OtherDeductions                  *Decimal `json:"other_deductions,omitempty"`
	OtherDeductionsFromNetPay        *Decimal `json:"other_deductions_from_net_pay,omitempty"`
	DeductionsSubjectToNICButNotPAYE *Decimal `json:"deductions_subject_to_nic_but_not_paye,omitempty"`
	DeductionFreePay                 *Decimal `json:"deduction_free_pay,omitempty"`
	Attachments                      *Decimal `json:"attachments,omitempty"`
	PayrollGiving                    *Decimal `json:"payroll_giving,omitempty"`

	// Student and postgraduate loans.
	StudentLoanDeduction     *Decimal `json:"student_loan_deduction,omitempty"`
	DeductStudentLoan        *bool    `json:"deduct_student_loan,omitempty"`
	StudentLoanDeductionPlan string   `json:"student_loan_deductions_plan,omitempty"`
	PostgradLoanDeduction    *Decimal `json:"postgrad_loan_deduction,omitempty"`
	DeductPostgradLoan       *bool    `json:"deduct_postgrad_loan,omitempty"`

	Week1Month1Basis *bool `json:"week_1_month_1_basis,omitempty"`
	LeavingPayslip   *bool `json:"leaving_payslip,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

Payslip is one employee's pay for one period. Every field is read-only.

The field list is long because RTI reporting is: statutory payments, pension arrangements and loan plans each need their own line.

type PriceListItem

type PriceListItem struct {
	URL ResourceURL `json:"url,omitempty"`

	// Code, Quantity, ItemType and Description are all required.
	Code string `json:"code,omitempty"`
	// ItemType is Hours, Days, Weeks, Months, Years, Products, Services,
	// Training, Expenses, Comment, Bills, Discount, Credit, VAT or Stock.
	ItemType    string   `json:"item_type,omitempty"`
	Description string   `json:"description,omitempty"`
	Quantity    *Decimal `json:"quantity,omitempty"`
	Price       *Decimal `json:"price,omitempty"`

	// VATStatus is UK only: out_of_scope (the default), reduced, standard or
	// zero. Universal and US accounts use the sales tax rates instead.
	VATStatus          string   `json:"vat_status,omitempty"`
	SalesTaxRate       *Decimal `json:"sales_tax_rate,omitempty"`
	SecondSalesTaxRate *Decimal `json:"second_sales_tax_rate,omitempty"`

	Category ResourceURL `json:"category,omitempty"`
	// StockItem is required when ItemType is Stock.
	StockItem ResourceURL `json:"stock_item,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

PriceListItem is a saved line that can be dropped onto an invoice or estimate.

See https://dev.freeagent.com/docs/price_list_items

type PriceListItemService

type PriceListItemService struct {
	Collection[PriceListItem]
}

PriceListItemService covers https://dev.freeagent.com/docs/price_list_items

type ProfitAndLoss

type ProfitAndLoss struct {
	From                         Date               `json:"from,omitzero"`
	To                           Date               `json:"to,omitzero"`
	Income                       *Decimal           `json:"income,omitempty"`
	Expenses                     *Decimal           `json:"expenses,omitempty"`
	OperatingProfit              *Decimal           `json:"operating_profit,omitempty"`
	Less                         []ProfitAndLossRow `json:"less,omitempty"`
	RetainedProfit               *Decimal           `json:"retained_profit,omitempty"`
	RetainedProfitBroughtForward *Decimal           `json:"retained_profit_brought_forward,omitempty"`
	RetainedProfitCarriedForward *Decimal           `json:"retained_profit_carried_forward,omitempty"`
}

ProfitAndLoss is the profit and loss summary.

Its money values arrive as quoted strings, unlike BalanceSheet and Cashflow which send bare numbers. Decimal accepts both, so the inconsistency does not reach callers.

type ProfitAndLossRow

type ProfitAndLossRow struct {
	Title string   `json:"title,omitempty"`
	Total *Decimal `json:"total,omitempty"`
}

ProfitAndLossRow is one deduction line under "less".

type Project

type Project struct {
	URL ResourceURL `json:"url,omitempty"`

	// Contact is required and references the contact to bill.
	Contact ResourceURL `json:"contact,omitempty"`
	Name    string      `json:"name,omitempty"`
	// Status is Active, Completed, Cancelled or Hidden.
	Status              string `json:"status,omitempty"`
	ContractPOReference string `json:"contract_po_reference,omitempty"`
	Currency            string `json:"currency,omitempty"`

	// Budget is required; send zero when the project has no budget.
	Budget *Decimal `json:"budget,omitempty"`
	// BudgetUnits is Hours, Days or Monetary.
	BudgetUnits       string   `json:"budget_units,omitempty"`
	HoursPerDay       *Decimal `json:"hours_per_day,omitempty"`
	NormalBillingRate *Decimal `json:"normal_billing_rate,omitempty"`
	// BillingPeriod is hour or day.
	BillingPeriod string `json:"billing_period,omitempty"`

	UsesProjectInvoiceSequence         *bool `json:"uses_project_invoice_sequence,omitempty"`
	IncludeUnbilledTimeInProfitability *bool `json:"include_unbilled_time_in_profitability,omitempty"`
	IsIR35                             *bool `json:"is_ir35,omitempty"`

	StartsOn Date `json:"starts_on,omitzero"`
	EndsOn   Date `json:"ends_on,omitzero"`

	// Read-only. ContactName is a display convenience the API adds to
	// responses; it is not accepted on write.
	ContactName string `json:"contact_name,omitempty"`
	IsDeletable *bool  `json:"is_deletable,omitempty"`
	CreatedAt   Time   `json:"created_at,omitzero"`
	UpdatedAt   Time   `json:"updated_at,omitzero"`
}

Project is a body of work billed to a contact.

See https://dev.freeagent.com/docs/projects

type ProjectService

type ProjectService struct {
	Collection[Project]
}

ProjectService covers https://dev.freeagent.com/docs/projects

type Property

type Property struct {
	URL ResourceURL `json:"url,omitempty"`

	// Name is absent from the documented attribute list but present in its
	// example response.
	Name string `json:"name,omitempty"`

	Address1 string `json:"address1,omitempty"`
	Address2 string `json:"address2,omitempty"`
	Address3 string `json:"address3,omitempty"`
	Town     string `json:"town,omitempty"`
	Region   string `json:"region,omitempty"`
	Postcode string `json:"postcode,omitempty"`
	// Country defaults to United Kingdom and cannot be overridden.
	Country string `json:"country,omitempty"`
}

Property is a rental property. Only UkUnincorporatedLandlord companies can hold them; on any other company type the collection is simply empty.

See https://dev.freeagent.com/docs/properties

type PropertyService

type PropertyService struct {
	Collection[Property]
}

PropertyService covers https://dev.freeagent.com/docs/properties

type RateLimit

type RateLimit struct {
	Limit     int
	Remaining int
	Reset     time.Time
}

RateLimit reports the server's own accounting, when it sends it. FreeAgent documents only Retry-After, so these fields are best effort and a zero value means the response carried no such header.

type ReadCollection

type ReadCollection[T any] struct {
	// contains filtered or unexported fields
}

ReadCollection is the read surface of a collection endpoint. Some families are genuinely read-only per record (bank transactions arrive by statement upload or feed, not by POST), so they embed this rather than Collection and the type system rules out the writes the API does not offer.

func (*ReadCollection[T]) All

func (c *ReadCollection[T]) All(ctx context.Context, opts *ListOptions) iter.Seq2[T, error]

All iterates the entire collection, following pagination. Iteration stops on the first error, which is yielded alongside the zero value; breaking out of the range loop stops it cleanly without further requests.

func (*ReadCollection[T]) Get

func (c *ReadCollection[T]) Get(ctx context.Context, id int64) (*T, *Response, error)

Get fetches one record by numeric id.

func (*ReadCollection[T]) GetURL

func (c *ReadCollection[T]) GetURL(ctx context.Context, ref ResourceURL) (*T, *Response, error)

GetURL fetches the record a payload reference points at. The URL must be on the client's own host: references come from API responses, so following one blindly would let an upstream response redirect the client elsewhere.

func (*ReadCollection[T]) List

func (c *ReadCollection[T]) List(ctx context.Context, opts *ListOptions) ([]T, *Response, error)

List fetches one page. Callers that want the whole collection should use All, which walks the Link header for them.

func (*ReadCollection[T]) Meta

func (c *ReadCollection[T]) Meta() ResourceMeta

Meta returns the resource metadata backing this collection.

type Reader

type Reader[T any] struct {
	// contains filtered or unexported fields
}

Reader is the read-only surface for singletons and reports, which have no id segment and no write verbs.

func (*Reader[T]) Get

func (r *Reader[T]) Get(ctx context.Context, params url.Values) (*T, *Response, error)

Get fetches the resource. params carries endpoint-specific filters such as from_date and to_date on the accounting reports.

func (*Reader[T]) Meta

func (r *Reader[T]) Meta() ResourceMeta

Meta returns the resource metadata backing this reader.

type RecurringInvoice

type RecurringInvoice struct {
	URL ResourceURL `json:"url,omitempty"`

	Contact     ResourceURL `json:"contact,omitempty"`
	ContactName string      `json:"contact_name,omitempty"`
	Reference   string      `json:"reference,omitempty"`
	DatedOn     Date        `json:"dated_on,omitzero"`

	// Frequency is Weekly, Two Weekly, Four Weekly, Monthly, Two Monthly,
	// Quarterly, Biannually, Annually or 2-Yearly.
	Frequency string `json:"frequency,omitempty"`
	// NextRecursOn is when the next invoice will be raised.
	NextRecursOn Date `json:"next_recurs_on,omitzero"`
	// RecurringEndDate is blank when the schedule runs forever.
	RecurringEndDate Date `json:"recurring_end_date,omitzero"`
	// RecurringStatus is Draft or Active.
	RecurringStatus string `json:"recurring_status,omitempty"`

	Currency           string   `json:"currency,omitempty"`
	ExchangeRate       *Decimal `json:"exchange_rate,omitempty"`
	NetValue           *Decimal `json:"net_value,omitempty"`
	SalesTaxValue      *Decimal `json:"sales_tax_value,omitempty"`
	TotalValue         *Decimal `json:"total_value,omitempty"`
	PaymentTermsInDays *int     `json:"payment_terms_in_days,omitempty"`

	OmitHeader           *bool `json:"omit_header,omitempty"`
	AlwaysShowBICAndIBAN *bool `json:"always_show_bic_and_iban,omitempty"`

	InvoiceItems   []InvoiceItem          `json:"invoice_items,omitempty"`
	PaymentMethods *InvoicePaymentMethods `json:"payment_methods,omitempty"`
}

RecurringInvoice is a template that generates invoices on a schedule.

The API exposes reads only: creating and editing one is done in the FreeAgent interface, which is why this family embeds ReadCollection.

See https://dev.freeagent.com/docs/recurring_invoices

type RecurringInvoiceService

type RecurringInvoiceService struct {
	ReadCollection[RecurringInvoice]
}

RecurringInvoiceService covers https://dev.freeagent.com/docs/recurring_invoices

type ReportOptions

type ReportOptions struct {
	FromDate Date
	ToDate   Date
	// AccountingPeriod selects a named period instead of a date range, for
	// the reports that support it.
	AccountingPeriod string
	// Extra carries anything not modelled here.
	Extra url.Values
}

ReportOptions bounds an accounting report. Most reports accept a date range; some also accept an accounting period reference instead.

Leaving everything unset asks for the report's own default, which is generally the current accounting year to date.

type ReportService

type ReportService struct {
	// contains filtered or unexported fields
}

ReportService covers the accounting reports. They are read-only, have no id segment, and each has its own response shape, so they are gathered here rather than forced through the collection generics.

func (*ReportService) BalanceSheet

func (s *ReportService) BalanceSheet(ctx context.Context, opts *ReportOptions) (*BalanceSheet, *Response, error)

BalanceSheet returns the balance sheet.

See https://dev.freeagent.com/docs/balance_sheet

func (*ReportService) Cashflow

func (s *ReportService) Cashflow(ctx context.Context, from, to Date) (*Cashflow, *Response, error)

Cashflow returns money in and out between two dates. Both are required.

Note the path: this one sits at the API root, not under accounting/.

See https://dev.freeagent.com/docs/cashflow

func (*ReportService) ProfitAndLoss

func (s *ReportService) ProfitAndLoss(ctx context.Context, opts *ReportOptions) (*ProfitAndLoss, *Response, error)

ProfitAndLoss returns the profit and loss summary.

See https://dev.freeagent.com/docs/profit_and_loss

func (*ReportService) TrialBalance

func (s *ReportService) TrialBalance(ctx context.Context, opts *ReportOptions) ([]TrialBalanceEntry, *Response, error)

TrialBalance returns the trial balance summary. Unlike the other reports it answers with an array rather than an object.

See https://dev.freeagent.com/docs/trial_balance

type ResourceMeta

type ResourceMeta struct {
	// Name is the registry key and the name facli accepts.
	Name string
	// Path is relative to the API root, for example "invoices" or
	// "accounting/profit_and_loss/summary".
	Path string
	// Singular is the JSON envelope key for a single record. Empty means the
	// endpoint returns an unenveloped body.
	Singular string
	// Plural is the JSON envelope key for a list. Empty for singletons.
	Plural string
	// Singleton marks endpoints with no id segment, such as company.
	Singleton bool
	// ReadOnly marks endpoints with no write verbs.
	ReadOnly bool
	// Grouped marks endpoints whose results are split across several
	// envelope keys instead of one plural key. Categories is the only one.
	Grouped bool
	// NoList marks families with no collection endpoint, such as
	// attachments, which are reached only through a parent record.
	NoList bool
	// RequiresBankAccount marks list endpoints that reject a request without
	// a bank_account filter.
	RequiresBankAccount bool
	// CustomEnvelope marks families whose response uses neither the singular
	// nor the plural key, such as payroll, which answers with periods and
	// payments. Their services decode the envelope themselves.
	CustomEnvelope bool
	// Doc is the upstream documentation URL.
	Doc string
}

ResourceMeta describes one FreeAgent resource family. The same metadata drives the typed services and the facli generic commands, so a resource is added in exactly one place.

func LookupResource

func LookupResource(name string) (ResourceMeta, bool)

LookupResource returns the metadata registered under name.

type ResourceURL

type ResourceURL string

ResourceURL is how FreeAgent identifies records. Every cross-reference in a payload is a full URL rather than a bare id, so this type carries them and extracts the parts callers actually need.

Example

Payload cross-references are URLs, not ids.

package main

import (
	"errors"
	"fmt"
	"log"

	"github.com/alekc/freeagent-sdk/freeagent"
)

func main() {
	ref := freeagent.ResourceURL("https://api.freeagent.com/v2/bank_accounts/1")

	id, err := ref.ID()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(id, ref.Kind())

	// A singleton is not a collection member, and Kind says so too.
	company := freeagent.ResourceURL("https://api.freeagent.com/v2/company")
	_, err = company.ID()
	fmt.Println(errors.Is(err, freeagent.ErrNotAMember), company.Kind() == "")
}
Output:
1 bank_accounts
true true

func (ResourceURL) ID

func (r ResourceURL) ID() (int64, error)

ID returns the numeric identifier of a collection member URL. Singleton URLs such as /v2/company return ErrNotAMember.

func (ResourceURL) IsZero

func (r ResourceURL) IsZero() bool

IsZero reports whether the reference is unset.

func (ResourceURL) Kind

func (r ResourceURL) Kind() string

Kind returns the collection segment of a member URL, for example "invoices" for .../v2/invoices/123. It returns "" when the URL is not a member URL, which makes it safe to use for routing a heterogeneous set of references.

func (ResourceURL) String

func (r ResourceURL) String() string

String returns the URL unchanged.

type Response

type Response struct {
	*http.Response

	// Page numbers extracted from the Link header. Zero means the relation
	// was absent, so NextPage == 0 is the end of the collection.
	FirstPage int
	PrevPage  int
	NextPage  int
	LastPage  int

	// TotalCount comes from X-Total-Count and is -1 when not sent.
	TotalCount int

	RateLimit RateLimit
}

Response wraps the HTTP response with the pagination and rate-limit state parsed out of its headers.

type RetryPolicy

type RetryPolicy struct {
	// MaxAttempts counts the first try. 1 disables retries.
	MaxAttempts int
	// BaseDelay is the first backoff interval, doubled per attempt.
	BaseDelay time.Duration
	// MaxDelay caps the computed backoff. It does not cap Retry-After.
	MaxDelay time.Duration
	// MaxRetryAfter is the longest server-requested wait that is honoured.
	// Beyond it the error is returned so the caller decides, rather than the
	// client blocking for an unbounded time.
	MaxRetryAfter time.Duration
}

RetryPolicy governs automatic retries. Retries apply to 429 on any method, and to transport errors and 5xx on idempotent methods only.

type SalesTaxPeriod

type SalesTaxPeriod struct {
	URL ResourceURL `json:"url,omitempty"`

	SalesTaxName string `json:"sales_tax_name,omitempty"`
	// SalesTaxRegistrationStatus is Registered or Not Registered.
	SalesTaxRegistrationStatus string   `json:"sales_tax_registration_status,omitempty"`
	SalesTaxRegistrationNumber string   `json:"sales_tax_registration_number,omitempty"`
	SalesTaxIsValueAdded       *bool    `json:"sales_tax_is_value_added,omitempty"`
	SalesTaxRate1              *Decimal `json:"sales_tax_rate_1,omitempty"`
	SalesTaxRate2              *Decimal `json:"sales_tax_rate_2,omitempty"`
	SalesTaxRate3              *Decimal `json:"sales_tax_rate_3,omitempty"`

	// Universal companies only.
	SecondSalesTaxName       string   `json:"second_sales_tax_name,omitempty"`
	SecondSalesTaxRate1      *Decimal `json:"second_sales_tax_rate_1,omitempty"`
	SecondSalesTaxRate2      *Decimal `json:"second_sales_tax_rate_2,omitempty"`
	SecondSalesTaxRate3      *Decimal `json:"second_sales_tax_rate_3,omitempty"`
	SecondSalesTaxIsCompound *bool    `json:"second_sales_tax_is_compound,omitempty"`

	EffectiveDate Date `json:"effective_date,omitzero"`

	// Read-only. A locked period cannot be deleted.
	IsLocked     *bool  `json:"is_locked,omitempty"`
	LockedReason string `json:"locked_reason,omitempty"`
}

SalesTaxPeriod is a dated set of sales tax rates.

US and Universal companies only: on a UK company the endpoint is a 404. Verifying it needs a sandbox company created with one of those types, which is a free test fixture rather than a real US business.

See https://dev.freeagent.com/docs/sales_tax_periods

type SalesTaxPeriodService

type SalesTaxPeriodService struct {
	Collection[SalesTaxPeriod]
}

SalesTaxPeriodService covers https://dev.freeagent.com/docs/sales_tax_periods

type SalesTaxService

type SalesTaxService struct {
	// contains filtered or unexported fields
}

SalesTaxService covers the one endpoint on https://dev.freeagent.com/docs/sales_tax

That page is mostly prose about how sales tax is expressed on other resources; the EC VAT MOSS rate lookup is the only endpoint it defines.

func (*SalesTaxService) ECMossRates

func (s *SalesTaxService) ECMossRates(ctx context.Context, country string, on Date) (*ECMossRates, *Response, error)

ECMossRates returns the VAT rates for an EU country on a date. Both are required; the country is a name such as "Ireland", not a code.

type StatementLine

type StatementLine struct {
	DatedOn     Date    `json:"dated_on"`
	Amount      Decimal `json:"amount"`
	Description string  `json:"description,omitempty"`
	// FitID is the bank's own identifier for the row. Supplying it lets
	// FreeAgent recognise a re-upload instead of duplicating the line.
	FitID string `json:"fitid,omitempty"`
}

StatementLine is one row of a statement upload.

This is deliberately not a BankTransaction: the upload takes a different and much smaller shape, and its amount is a JSON number where every other money value in the API is a quoted string. Posting a BankTransaction here is accepted with a 200 and then silently imports nothing.

func (StatementLine) MarshalJSON

func (l StatementLine) MarshalJSON() ([]byte, error)

MarshalJSON writes amount unquoted. Decimal marshals as a string by design, which is right everywhere else and wrong here.

type StockItem

type StockItem struct {
	URL ResourceURL `json:"url,omitempty"`

	// Description doubles as the item code shown on invoices and estimates.
	Description string `json:"description,omitempty"`
	// CostOfSaleCategory is the spending category sales are accounted to.
	CostOfSaleCategory ResourceURL `json:"cost_of_sale_category,omitempty"`

	// Opening figures are as at the FreeAgent start date.
	OpeningQuantity *Decimal `json:"opening_quantity,omitempty"`
	OpeningBalance  *Decimal `json:"opening_balance,omitempty"`

	// StockOnHand is read-only and moves as stock is bought and sold.
	StockOnHand *Decimal `json:"stock_on_hand,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

StockItem is something the company buys and sells by quantity.

The quantity fields are documented as integers but arrive as quoted decimals ("10.0"), so they are Decimal here.

See https://dev.freeagent.com/docs/stock_items

type StockItemService

type StockItemService struct {
	ReadCollection[StockItem]
}

StockItemService covers https://dev.freeagent.com/docs/stock_items

Read-only: the documentation lists only GET endpoints, and POST answers 404 Not Found rather than 405, so stock items are created in the FreeAgent interface or as a side effect of a stock purchase.

type Task

type Task struct {
	URL ResourceURL `json:"url,omitempty"`

	Project  ResourceURL `json:"project,omitempty"`
	Name     string      `json:"name,omitempty"`
	Currency string      `json:"currency,omitempty"`
	// Status is Active, Completed or Hidden.
	Status     string `json:"status,omitempty"`
	IsBillable *bool  `json:"is_billable,omitempty"`

	// Needs the Contacts and Projects permission. BillingPeriod is day or
	// hour.
	BillingRate   *Decimal `json:"billing_rate,omitempty"`
	BillingPeriod string   `json:"billing_period,omitempty"`

	// Read-only.
	IsDeletable *bool `json:"is_deletable,omitempty"`
	CreatedAt   Time  `json:"created_at,omitzero"`
	UpdatedAt   Time  `json:"updated_at,omitzero"`
}

Task is a unit of work within a project, used to bill timeslips.

See https://dev.freeagent.com/docs/tasks

type TaskService

type TaskService struct {
	Collection[Task]
}

TaskService covers https://dev.freeagent.com/docs/tasks

Creating a task differs from the other collections: the parent project goes in the query string rather than the body, so CreateForProject replaces the inherited Create.

func (*TaskService) Create

func (s *TaskService) Create(context.Context, *Task) (*Task, *Response, error)

Create is not available on tasks: the API takes the parent project as a query parameter, so this shadows the inherited Create rather than letting it silently post a task with no project. Use CreateForProject.

func (*TaskService) CreateForProject

func (s *TaskService) CreateForProject(ctx context.Context, project ResourceURL, in *Task) (*Task, *Response, error)

CreateForProject posts a new task under the given project.

type TaxTimelineItem

type TaxTimelineItem struct {
	Description string   `json:"description,omitempty"`
	Nature      string   `json:"nature,omitempty"`
	DatedOn     Date     `json:"dated_on,omitzero"`
	AmountDue   *Decimal `json:"amount_due,omitempty"`
	IsPersonal  *bool    `json:"is_personal,omitempty"`
}

TaxTimelineItem is an upcoming tax event.

type Time

type Time struct {
	time.Time
}

Time is an instant, serialised as ISO 8601 with milliseconds in UTC. The zero value marshals to null.

func ParseTime

func ParseTime(s string) (Time, error)

ParseTime accepts the timestamp forms FreeAgent has been observed to emit.

func TimeOf

func TimeOf(t time.Time) Time

TimeOf wraps a standard time.

func (Time) MarshalJSON

func (t Time) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (Time) String

func (t Time) String() string

String renders the timestamp in wire format, or "" when zero.

func (*Time) UnmarshalJSON

func (t *Time) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler.

type Timeslip

type Timeslip struct {
	URL ResourceURL `json:"url,omitempty"`

	// User, Project, Task and DatedOn are all required.
	User    ResourceURL `json:"user,omitempty"`
	Project ResourceURL `json:"project,omitempty"`
	Task    ResourceURL `json:"task,omitempty"`
	DatedOn Date        `json:"dated_on,omitzero"`
	// Hours is decimal, so 1:30 is 1.5.
	Hours   *Decimal `json:"hours,omitempty"`
	Comment string   `json:"comment,omitempty"`

	// Read-only.
	BilledOnInvoice ResourceURL    `json:"billed_on_invoice,omitempty"`
	Timer           *TimeslipTimer `json:"timer,omitempty"`
	CreatedAt       Time           `json:"created_at,omitzero"`
	UpdatedAt       Time           `json:"updated_at,omitzero"`
}

Timeslip is time recorded against a task.

See https://dev.freeagent.com/docs/timeslips

type TimeslipService

type TimeslipService struct {
	Collection[Timeslip]
}

TimeslipService covers https://dev.freeagent.com/docs/timeslips

func (*TimeslipService) StartTimer

func (s *TimeslipService) StartTimer(ctx context.Context, id int64) (*Timeslip, *Response, error)

StartTimer starts the timer on a timeslip.

func (*TimeslipService) StopTimer

func (s *TimeslipService) StopTimer(ctx context.Context, id int64) (*Timeslip, *Response, error)

StopTimer stops the timer, folding the elapsed time into the timeslip.

type TimeslipTimer

type TimeslipTimer struct {
	Running *bool `json:"running,omitempty"`
	// StartFrom is the effective start, which already accounts for any time
	// logged before the timer was started.
	StartFrom Time `json:"start_from,omitzero"`
}

TimeslipTimer is present only while a timer is running on the timeslip.

type TokenSource

type TokenSource struct {
	// contains filtered or unexported fields
}

TokenSource refreshes access tokens and persists the rotated refresh token. FreeAgent issues a new refresh token on every refresh, so a source that does not write the new one back loses access as soon as the old one is retired. It satisfies oauth2.TokenSource and is safe for concurrent use.

func NewTokenSource

func NewTokenSource(ctx context.Context, cfg *oauth2.Config, store TokenStore) (*TokenSource, error)

NewTokenSource builds a refreshing source over a store. ctx governs the refresh requests and may carry an oauth2.HTTPClient override.

func (*TokenSource) AuthCodeURL

func (s *TokenSource) AuthCodeURL(state string) string

AuthCodeURL builds the URL the user visits to approve the app.

func (*TokenSource) Exchange

func (s *TokenSource) Exchange(ctx context.Context, code string) (*oauth2.Token, error)

Exchange trades an authorisation code for a token and stores it. It is the final step of the interactive login flow.

func (*TokenSource) Peek

func (s *TokenSource) Peek() (*oauth2.Token, error)

Peek returns the cached token without triggering a refresh, loading from the store on first use. Tooling uses it to report expiry.

func (*TokenSource) Token

func (s *TokenSource) Token() (*oauth2.Token, error)

Token returns a valid access token, refreshing and persisting when needed. The lock is deliberately held across the refresh: FreeAgent allows only 15 refreshes per minute, so serialising concurrent callers onto one refresh is the correct behaviour rather than a bottleneck to optimise away.

type TokenStore

type TokenStore interface {
	Load(ctx context.Context) (*oauth2.Token, error)
	Save(ctx context.Context, token *oauth2.Token) error
}

TokenStore persists OAuth tokens between processes. Load returns ErrNoToken when nothing has been stored yet.

type Transaction

type Transaction struct {
	URL ResourceURL `json:"url,omitempty"`

	DatedOn     Date   `json:"dated_on,omitzero"`
	Description string `json:"description,omitempty"`

	Category     ResourceURL `json:"category,omitempty"`
	CategoryName string      `json:"category_name,omitempty"`
	NominalCode  string      `json:"nominal_code,omitempty"`
	// DebitValue is negative for a credit.
	DebitValue *Decimal `json:"debit_value,omitempty"`
	// SourceItemURL points at whatever produced the entry, such as a bank
	// transaction explanation. It may be absent.
	SourceItemURL ResourceURL `json:"source_item_url,omitempty"`
	// ForeignCurrencyData is an empty object unless foreign currency was
	// involved.
	ForeignCurrencyData *TransactionForeignCurrency `json:"foreign_currency_data,omitempty"`

	CreatedAt Time `json:"created_at,omitzero"`
	UpdatedAt Time `json:"updated_at,omitzero"`
}

Transaction is a posted accounting entry. Transactions are generated by FreeAgent from invoices, bills, explanations and journals, so this family is read-only.

See https://dev.freeagent.com/docs/transactions

type TransactionForeignCurrency

type TransactionForeignCurrency struct {
	CurrencyCode string   `json:"currency_code,omitempty"`
	DebitValue   *Decimal `json:"debit_value,omitempty"`
}

TransactionForeignCurrency carries the original currency amounts.

type TransactionService

type TransactionService struct {
	ReadCollection[Transaction]
}

TransactionService covers https://dev.freeagent.com/docs/transactions

Note the path: transactions live under accounting/, not at the API root.

The date range is more constrained than the documented 12-month limit suggests: it must fall inside a single accounting period. A range that starts before the company's first period returns 400 "No accounting period includes the specified dates", so derive the bounds from Company.AnnualAccountingPeriods rather than from today's date.

type TrialBalanceEntry

type TrialBalanceEntry struct {
	Category ResourceURL `json:"category,omitempty"`
	// NominalCode is the internal code, which for sub-accounts embeds the
	// underlying record id, for example "750-47915". DisplayNominalCode is
	// the one shown to users, "750-1". They are not interchangeable.
	NominalCode        string      `json:"nominal_code,omitempty"`
	DisplayNominalCode string      `json:"display_nominal_code,omitempty"`
	Name               string      `json:"name,omitempty"`
	Total              *Decimal    `json:"total,omitempty"`
	BankAccount        ResourceURL `json:"bank_account,omitempty"`
	User               ResourceURL `json:"user,omitempty"`
	StockItem          ResourceURL `json:"stock_item,omitempty"`
}

TrialBalanceEntry is one line of the trial balance.

type User

type User struct {
	URL ResourceURL `json:"url,omitempty"`

	Email     string `json:"email,omitempty"`
	FirstName string `json:"first_name,omitempty"`
	LastName  string `json:"last_name,omitempty"`
	// Role is Owner, Director, Partner, Company Secretary, Employee,
	// Shareholder or Accountant.
	Role string `json:"role,omitempty"`

	NINumber           string `json:"ni_number,omitempty"`
	UniqueTaxReference string `json:"unique_tax_reference,omitempty"`

	OpeningMileage *Decimal `json:"opening_mileage,omitempty"`
	// PermissionLevel is 0 to 8; see the access levels in the API docs.
	PermissionLevel *int `json:"permission_level,omitempty"`
	// SendInvitation asks FreeAgent to email a password invitation. Write-only.
	SendInvitation *bool `json:"send_invitation,omitempty"`

	// Hidden is undocumented but returned by the live API.
	Hidden *bool `json:"hidden,omitempty"`

	// Read-only.
	CurrentPayrollProfile *UserPayrollProfile `json:"current_payroll_profile,omitempty"`
	CreatedAt             Time                `json:"created_at,omitzero"`
	UpdatedAt             Time                `json:"updated_at,omitzero"`
}

User is a person with access to the FreeAgent account.

See https://dev.freeagent.com/docs/users

type UserPayrollProfile

type UserPayrollProfile struct {
	TotalPayInPreviousEmployment *Decimal `json:"total_pay_in_previous_employment,omitempty"`
	TotalTaxInPreviousEmployment *Decimal `json:"total_tax_in_previous_employment,omitempty"`
}

UserPayrollProfile is the read-only payroll summary present when a profile has been set for the current tax year.

type UserService

type UserService struct {
	Collection[User]
}

UserService covers https://dev.freeagent.com/docs/users

func (*UserService) Me

func (s *UserService) Me(ctx context.Context) (*User, *Response, error)

Me returns the user the access token belongs to.

func (*UserService) UpdateMe

func (s *UserService) UpdateMe(ctx context.Context, in *User) (*User, *Response, error)

UpdateMe updates the authenticated user.

type VATBreakdown

type VATBreakdown struct {
	Title string            `json:"title,omitempty"`
	Rows  []VATBreakdownRow `json:"rows,omitempty"`
}

VATBreakdown is the box-by-box detail of a return.

type VATBreakdownRow

type VATBreakdownRow struct {
	Title string   `json:"title,omitempty"`
	Value *Decimal `json:"value,omitempty"`
	Key   string   `json:"key,omitempty"`
	// BoxNumber is documented as a string but the live API sends a bare
	// number, which would fail to decode into one. Int64 takes either.
	BoxNumber Int64 `json:"box_number,omitempty"`
}

VATBreakdownRow is one box on the return.

type VATReturn

type VATReturn struct {
	// URL ends in the period end date, so ResourceURL.ID does not apply.
	URL ResourceURL `json:"url,omitempty"`

	PeriodStartsOn Date `json:"period_starts_on,omitzero"`
	PeriodEndsOn   Date `json:"period_ends_on,omitzero"`
	FilingDueOn    Date `json:"filing_due_on,omitzero"`

	// FilingStatus is unfiled, pending, rejected, filed or marked_as_filed.
	FilingStatus   string `json:"filing_status,omitempty"`
	FiledAt        Time   `json:"filed_at,omitzero"`
	FiledReference string `json:"filed_reference,omitempty"`

	Payments  []FilingPayment `json:"payments,omitempty"`
	Breakdown *VATBreakdown   `json:"breakdown,omitempty"`
}

VATReturn is one VAT period and its filing state.

See https://dev.freeagent.com/docs/vat_returns

type VATReturnService

type VATReturnService struct {
	// contains filtered or unexported fields
}

VATReturnService covers https://dev.freeagent.com/docs/vat_returns

Returns are addressed by period end date. The account must be VAT registered; otherwise the collection is empty.

func (*VATReturnService) Get

func (s *VATReturnService) Get(ctx context.Context, periodEndsOn Date) (*VATReturn, *Response, error)

Get fetches one period by its end date.

func (*VATReturnService) List

List returns every VAT period.

func (*VATReturnService) MarkAsFiled

func (s *VATReturnService) MarkAsFiled(ctx context.Context, periodEndsOn Date) (*VATReturn, *Response, error)

MarkAsFiled records the return as filed outside FreeAgent. Needs Full Access.

func (*VATReturnService) MarkAsUnfiled

func (s *VATReturnService) MarkAsUnfiled(ctx context.Context, periodEndsOn Date) (*VATReturn, *Response, error)

MarkAsUnfiled reverses MarkAsFiled.

func (*VATReturnService) MarkPaymentAsPaid

func (s *VATReturnService) MarkPaymentAsPaid(ctx context.Context, periodEndsOn, paymentDate Date) (*VATReturn, *Response, error)

MarkPaymentAsPaid records the payment due on paymentDate as settled.

func (*VATReturnService) MarkPaymentAsUnpaid

func (s *VATReturnService) MarkPaymentAsUnpaid(ctx context.Context, periodEndsOn, paymentDate Date) (*VATReturn, *Response, error)

MarkPaymentAsUnpaid reverses MarkPaymentAsPaid.

func (*VATReturnService) Meta

func (s *VATReturnService) Meta() ResourceMeta

Meta returns the resource metadata.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL