AnchorAnchor

API Reference

REST API for Anchor: authenticate with API keys, manage invoices, contacts, and journals, and use webhooks for South African small-business books.

Anchor provides a full REST API for programmatic access to all features.

Authentication

Create API keys in Settings > API Keys, then include in requests:

curl -H "Authorization: Bearer your-api-key" \
     -H "x-organization-id: org-id" \
     https://your-instance.com/api/v1/contacts

Organization Context

All API routes require an organization context via the x-organization-id header:

x-organization-id: your-org-id

Account password

Password change and reset are session or public-auth routes, not API-key operations. Bearer API keys receive 401 on POST /api/v1/user/password. Keys stay valid after a password write — rotate them under Settings → API Keys.

MethodPathNotes
POST/api/v1/user/passwordSigned-in session only. Current password required when a hash exists; OAuth-only users may set a first password. TOTP/backup code required if 2FA is enabled. Always re-authenticate after 200.
POST/api/auth/forgot-passwordPublic. Valid email always returns the same 200 body: If an account exists for that email, we sent a reset link. Does not say whether the address is registered.
POST/api/auth/reset-passwordPublic. Consumes a single-use token. Does not create a session and does not skip 2FA.
GET/PATCH/api/v1/user/profileIncludes hasPassword (boolean). Never returns a hash. PATCH still does not accept a password field.

Agents can read hasPassword via MCP get_password_status (or WebMCP anchor_get_context). There is no MCP tool that accepts a password or reset token. User guide: Password. Likewise get_email_change_status (or user.emailChange in anchor_get_context) reports a sign-in email change in progress, masked; no tool requests, approves or confirms one (Sign-in email).

Base URL

/api/v1/

Response Format

{
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 100,
    "totalPages": 5
  }
}
{
  "error": "Description of the error"
}

Endpoints

Contacts

MethodPathDescription
GET/api/v1/contactsList contacts
POST/api/v1/contactsCreate contact
GET/api/v1/contacts/:idGet contact
PATCH/api/v1/contacts/:idUpdate contact
DELETE/api/v1/contacts/:idDelete contact
GET/api/v1/contacts/:id/statementContact statement of account (startDate/endDate YYYY-MM-DD; default last 12 months). Amounts integer cents
GET/api/v1/contacts/:id/statement/pdfSame statement as PDF
POST/api/v1/contacts/:id/statement/emailEmail the statement to the contact (startDate/endDate optional)
GET/api/v1/contacts/:id/supplier-statementSupplier (AP) statement only

Invoices

MethodPathDescription
GET/api/v1/invoicesList invoices
POST/api/v1/invoicesCreate invoice (optional invoiceNumber; auto INV-##### if omitted)
GET/api/v1/invoices/:idGet invoice
PATCH/api/v1/invoices/:idUpdate draft invoice (header and/or replace lines). Sales-order invoices cannot replace lines
DELETE/api/v1/invoices/:idDelete invoice
POST/api/v1/invoices/:id/sendSend invoice
POST/api/v1/invoices/:id/payRecord payment
POST/api/v1/invoices/:id/voidVoid invoice
GET/api/v1/invoices/:id/pdfformat=pdf is the tax invoice. variant=pod is a proof of delivery (not a tax invoice; does not post). variant=with-pod is one PDF (tax invoice, then proof of delivery) and only with format=pdf

Quotes

MethodPathDescription
GET/api/v1/quotesList quotes
POST/api/v1/quotesCreate quote
GET/api/v1/quotes/:idGet quote
PATCH/api/v1/quotes/:idUpdate draft quote (header and/or replace lines)
POST/api/v1/quotes/:id/sendSend quote
POST/api/v1/quotes/:id/acceptAccept quote
POST/api/v1/quotes/:id/declineDecline quote
POST/api/v1/quotes/:id/convertConvert to invoice (blocked if the quote is already a sales order)
POST/api/v1/quotes/:id/convert-to-sales-orderConvert accepted quote to a draft sales order (blocked if the quote already has invoices)

Sales Orders

MethodPathDescription
GET/api/v1/sales-ordersList sales orders (status, contactId, quoteId)
POST/api/v1/sales-ordersCreate draft. Optional quoteId converts an accepted quote. Does not reserve
GET/api/v1/sales-orders/:idGet order with lines and remaining-to-deliver / remaining-to-invoice
PATCH/api/v1/sales-orders/:idUpdate draft (header and/or replace lines)
DELETE/api/v1/sales-orders/:idSoft-delete draft
POST/api/v1/sales-orders/:id/sendSend: always reserves stock; email optional. Not a sale
POST/api/v1/sales-orders/:id/voidCancel (no live delivery notes or invoices)
POST/api/v1/sales-orders/:id/closeClose leftover undelivered qty (400 if unbilled deliveries)
POST/api/v1/sales-orders/:id/create-invoiceDraft tax invoice. Optional lines of human units (salesOrderLineId and quantity). Omit lines to invoice all remaining
GET/api/v1/sales-orders/:id/pdfSales order PDF (not a tax invoice)

Delivery Notes

MethodPathDescription
GET/api/v1/delivery-notesList GDNs (salesOrderId, status)
POST/api/v1/delivery-notesIssue immediately (stock out + DR GDNI / CR Inventory). Not a tax invoice
GET/api/v1/delivery-notes/:idGet delivery note
POST/api/v1/delivery-notes/:id/sendEmail PDF (no stock change)
POST/api/v1/delivery-notes/:id/voidCancel GDN (restock; blocked if invoiced)
GET/api/v1/delivery-notes/:id/pdfDelivery note PDF with no prices. Not a priced invoice. Footer: “This is not a tax invoice.”

Bills

MethodPathDescription
GET/api/v1/billsList bills
POST/api/v1/billsCreate bill
GET/api/v1/bills/:idGet bill
PATCH/api/v1/bills/:idUpdate draft bill (header and/or replace lines)
POST/api/v1/bills/:id/receiveReceive bill
POST/api/v1/bills/:id/payRecord payment
POST/api/v1/bills/:id/voidVoid bill

Purchase Orders

MethodPathDescription
GET/api/v1/purchase-ordersList POs
POST/api/v1/purchase-ordersCreate PO
GET/api/v1/purchase-orders/:idGet PO
POST/api/v1/purchase-orders/:id/sendSend PO
POST/api/v1/purchase-orders/:id/convertConvert to bill

Chart of Accounts

MethodPathDescription
GET/api/v1/accountsList accounts
POST/api/v1/accountsCreate account
GET/api/v1/accounts/:idGet account
PATCH/api/v1/accounts/:idUpdate account

Control Account Mappings

MethodPathDescription
GET/api/v1/settings/control-accountsList role → account mappings (?phase=1|2|3)
PUT/api/v1/settings/control-accountsSet mappings ({ mappings: [{ role, accountId }] }; accountId null resets)

Journal Entries

MethodPathDescription
GET/api/v1/entriesList entries (page, limit up to 200; optional source = manual or all, status, q, from, to, sort = created:desc (default), date:asc/desc, number:asc/desc, amount:asc/desc). Returns total, counts per status and totalPosted for every match
POST/api/v1/entriesCreate entry
GET/api/v1/entries/:idGet entry
POST/api/v1/entries/:id/postPost entry
POST/api/v1/entries/:id/voidVoid entry

Banking

MethodPathDescription
GET/api/v1/bank-accountsList bank accounts
POST/api/v1/bank-accountsCreate bank account
GET/api/v1/bank-accounts/:id/transactionsList transactions (page, limit up to 1000; optional status, startDate, endDate; include=raw adds each line's imported row and dedupe hash; summary=1 adds counts per status and money in/out in cents)
GET/api/v1/bank-accounts/:id/importsList statement imports
POST/api/v1/bank-accounts/:id/transactions/importPreview or import a statement file
GET/api/v1/bank-imports/:idGet statement import details
POST/api/v1/bank-transactions/:id/reconcileReconcile
POST/api/v1/bank-transactions/:id/excludeExclude

Expenses

MethodPathDescription
GET/api/v1/expensesList claims
POST/api/v1/expensesCreate claim
POST/api/v1/expenses/:id/submitSubmit
POST/api/v1/expenses/:id/approveApprove
POST/api/v1/expenses/:id/rejectReject
POST/api/v1/expenses/:id/payMark paid

Reports

MethodPathDescription
GET/api/v1/reports/trial-balanceTrial Balance
GET/api/v1/reports/balance-sheetBalance Sheet
GET/api/v1/reports/income-statementIncome Statement
GET/api/v1/reports/profit-and-lossProfit & Loss
GET/api/v1/reports/spend-by-accountSpend on expense accounts for a period (accountIds, from, to; cents)
GET/api/v1/search/documentsSearch invoices, bills, quotes and credit notes by meaning (q; optional types, from, to, amountCents, toleranceCents, status, limit)
GET/api/v1/search/contactsSearch contacts by meaning (q; optional type, limit)
GET/api/v1/help/searchSearch the help pages (q; optional limit)
GET/api/v1/aria/statusWhether ARIA can answer for this member (enabled, reason)
POST/api/v1/aria/askAsk ARIA (question; optional threadId, intent, context.accountIds, context.bankTransactionId). 404 while ARIA is off
GET/api/v1/reports/aged-receivablesAged Receivables
GET/api/v1/reports/aged-payablesAged Payables
GET/api/v1/reports/general-ledgerGeneral Ledger (startDate, endDate, optional accountId, costCenterId/projectId). format=pdf|xlsx downloads every line in the period; with accountId, just that account
GET/api/v1/reports/cash-flowCash Flow
GET/api/v1/reports/budget-vs-actualBudget vs Actual

Other Modules

MethodPathDescription
GET/POST/api/v1/inventoryInventory CRUD. GET includes quantityReserved and quantityAvailable (on-hand minus reserved)
POST/api/v1/inventory/:id/adjustAdjust stock
POST/api/v1/inventory/images/presignProduct picture upload: send fileName, mimeType, fileSize (PNG, JPEG, WebP or GIF, up to 2 MB); returns uploadUrl and fileKey. PUT the file to uploadUrl, then pass fileKey as imageUrl when creating or updating the item (null removes the picture). Items return imagePreviewUrl, a short-lived link
GET/POST/api/v1/projectsProjects CRUD
GET/POST/api/v1/projects/:id/time-entriesTime entries
POST/api/v1/projects/:id/invoiceGenerate invoice
GET/POST/api/v1/fixed-assetsFixed assets CRUD
POST/api/v1/fixed-assets/:id/depreciateDepreciate
POST/api/v1/fixed-assets/:id/disposeDispose
GET/api/v1/reports/fixed-asset-registerFixed asset register as-at (asAt, optional periodStart/periodEnd, format=pdf|xlsx)
GET/POST/api/v1/payroll/employeesEmployees CRUD
GET/POST/api/v1/payroll/runsPayroll runs
POST/api/v1/payroll/runs/:id/processProcess payroll
GET/POST/api/v1/budgetsBudgets CRUD
GET/api/v1/audit-logAudit log

See the API Integration Guide for detailed examples and authentication setup.

On this page