Lekker Accounting API v1

Lekker Accounting API

A REST API for everything you can do in Lekker Accounting: clients, products, invoices, quotes, purchase orders, payments and expenses. All requests and responses are JSON. Amount fields are returned as decimal strings (e.g. "1500.00") to avoid floating point surprises; dates are YYYY-MM-DD and timestamps are ISO-8601 UTC.

Base URL

https://lekkeraccounting.com/api/v1

Prefer machine-readable docs? Import the OpenAPI 3.1 document into Postman, Insomnia or your code generator.

Authentication

The API uses personal access tokens. Create one in the app under Settings → Claude — choose a name, whether the token can write, and an optional expiry. The token is shown once; store it securely. Send it on every request as a bearer token:

curl 'https://lekkeraccounting.com/api/v1/me' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Abilities. Tokens carry abilities: read allows all GET endpoints; write is additionally required for POST, PUT and DELETE. A read-only token calling a write endpoint gets a 403.

Revocation. Revoking a token in Settings → Claude immediately ends its access. Expired tokens are rejected with 401.

Workspaces & permissions

Every token is pinned to the workspace it was created in. All data you read or write through that token belongs to that workspace — switching your active workspace in the app does not affect existing tokens. If you belong to several businesses, create one token per workspace.

Tokens act with your role's permissions (admin, accountant or viewer). For example, listing clients needs manage_clients; a viewer-role user gets 403 regardless of token abilities. Each resource below lists the permission it requires.

Use GET /me to see the workspace, abilities and permissions a token has.

Pagination, sorting & filtering

List endpoints are paginated: ?page=2&per_page=50 (max 100 per page, default 25). Responses carry Laravel's standard data / links / meta envelope.

Sort with ?sort=<key>&direction=asc|desc — each resource documents its sort keys. Filters (search, status, date ranges) are plain query parameters listed per endpoint.

Soft deletes & restore

DELETE never destroys data — records move to the trash (also visible in the app under Trash) and get a deleted_at timestamp. Restore one with POST /<resource>/{id}/restore.

Deleted records are hidden from lists by default. Add ?trashed=with to include them or ?trashed=only to list the trash itself.

Errors

Errors are JSON with a human-readable message; validation errors add per-field detail:

{
  "message": "The client id field is required. (and 1 more error)",
  "errors": {
    "client_id": ["The client id field is required."],
    "items": ["The items field is required."]
  }
}
StatusMeaning
401Missing, invalid, expired or revoked token.
402The workspace's subscription is unpaid — API access is paused until it's settled.
403The token lacks the required ability, your role lacks the permission, or a plan limit blocks the action.
404Not found — including records that belong to another workspace.
422Validation failed; see errors.
429Rate limit exceeded — retry after the Retry-After seconds.

Rate limits

Requests are limited to 60 per minute per user. The X-RateLimit-Limit and X-RateLimit-Remaining headers show where you stand; exceeding the limit returns 429.

Me

Token introspection. Confirms the token works and shows who it acts as, which workspace it is pinned to, its abilities and the user's permissions.

GET /v1/me

Inspect the current token

Returns the authenticated user, the workspace the token operates on, token metadata and the user's permission names. Workspace includes invoice_allowance: monthly slots remaining, extra invoices granted by an admin (never expire, used after the monthly allowance), and when the monthly count resets.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/me' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "user": {
            "id": 12,
            "name": "Thandi Mokoena",
            "email": "thandi@example.com"
        },
        "workspace": {
            "id": "4d81…",
            "name": "Mokoena Consulting",
            "invoice_allowance": {
                "unlimited": false,
                "monthly_allowance": 50,
                "used_this_month": 12,
                "monthly_remaining": 38,
                "bonus_remaining": 500,
                "total_remaining": 538,
                "resets_on": "2026-09-01"
            }
        },
        "token": {
            "name": "Zapier integration",
            "abilities": [
                "read",
                "write"
            ],
            "last_used_at": "2026-07-16T08:12:00Z",
            "expires_at": null
        },
        "permissions": [
            "manage_clients",
            "manage_invoices",
            "manage_quotes"
        ]
    }
}

Clients

The people and businesses you invoice. Requires the `manage_clients` permission.

GET /v1/clients requires: read

List clients

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `name`, `company`, `email`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
search string Matches first name, last name, company name or email.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/clients' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 7,
            "first_name": "Thandi",
            "last_name": "Mokoena",
            "company_name": "Mokoena Consulting",
            "display_name": "Mokoena Consulting",
            "email": "accounts@mokoena.example",
            "contact": "0821234567",
            "region_code": "27",
            "vat_no": "4123456789",
            "website": null,
            "address": "12 Long Street",
            "city": "Cape Town",
            "state": "Western Cape",
            "postal_code": "8001",
            "country_code": "ZA",
            "note": null,
            "created_at": "2026-07-01T09:30:00Z",
            "updated_at": "2026-07-01T09:30:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/clients/{client} requires: read

Get a client

Returns a single client by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/clients/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 7,
        "first_name": "Thandi",
        "last_name": "Mokoena",
        "company_name": "Mokoena Consulting",
        "display_name": "Mokoena Consulting",
        "email": "accounts@mokoena.example",
        "contact": "0821234567",
        "region_code": "27",
        "vat_no": "4123456789",
        "website": null,
        "address": "12 Long Street",
        "city": "Cape Town",
        "state": "Western Cape",
        "postal_code": "8001",
        "country_code": "ZA",
        "note": null,
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
POST /v1/clients requires: write

Create a client

Creates a client. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

first_name string required Contact first name.
last_name string required Contact last name.
company_name string Company name — used as the display name when present.
email string Billing email address.
contact string Local phone number, without the country prefix.
region_code string Country dialling code (international calling prefix), digits only, e.g. `27` for South Africa.
vat_no string VAT registration number.
website string Website URL.
address string Street address.
city string City.
state string Province / region.
postal_code string Postal code.
country_code string ISO 3166-1 alpha-2 country code, e.g. `ZA` or `NA`.
note string Internal note (max 1000 characters).

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/clients' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"first_name":"Example","last_name":"Example"}'

Example response 201

{
    "data": {
        "id": 7,
        "first_name": "Thandi",
        "last_name": "Mokoena",
        "company_name": "Mokoena Consulting",
        "display_name": "Mokoena Consulting",
        "email": "accounts@mokoena.example",
        "contact": "0821234567",
        "region_code": "27",
        "vat_no": "4123456789",
        "website": null,
        "address": "12 Long Street",
        "city": "Cape Town",
        "state": "Western Cape",
        "postal_code": "8001",
        "country_code": "ZA",
        "note": null,
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
PUT /v1/clients/{client} requires: write

Update a client

Full update — send every field you want kept, exactly like create.

Body parameters

first_name string required Contact first name.
last_name string required Contact last name.
company_name string Company name — used as the display name when present.
email string Billing email address.
contact string Local phone number, without the country prefix.
region_code string Country dialling code (international calling prefix), digits only, e.g. `27` for South Africa.
vat_no string VAT registration number.
website string Website URL.
address string Street address.
city string City.
state string Province / region.
postal_code string Postal code.
country_code string ISO 3166-1 alpha-2 country code, e.g. `ZA` or `NA`.
note string Internal note (max 1000 characters).

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/clients/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"first_name":"Example","last_name":"Example"}'

Example response 200

{
    "data": {
        "id": 7,
        "first_name": "Thandi",
        "last_name": "Mokoena",
        "company_name": "Mokoena Consulting",
        "display_name": "Mokoena Consulting",
        "email": "accounts@mokoena.example",
        "contact": "0821234567",
        "region_code": "27",
        "vat_no": "4123456789",
        "website": null,
        "address": "12 Long Street",
        "city": "Cape Town",
        "state": "Western Cape",
        "postal_code": "8001",
        "country_code": "ZA",
        "note": null,
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
DELETE /v1/clients/{client} requires: write

Delete a client

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/clients/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/clients/{id}/restore requires: write

Restore a client

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/clients/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 7,
        "first_name": "Thandi",
        "last_name": "Mokoena",
        "company_name": "Mokoena Consulting",
        "display_name": "Mokoena Consulting",
        "email": "accounts@mokoena.example",
        "contact": "0821234567",
        "region_code": "27",
        "vat_no": "4123456789",
        "website": null,
        "address": "12 Long Street",
        "city": "Cape Town",
        "state": "Western Cape",
        "postal_code": "8001",
        "country_code": "ZA",
        "note": null,
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}

Products

Products and services used as invoice and quote line items. Requires the `manage_products` permission.

GET /v1/products requires: read

List products

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `name`, `code`, `unit_price`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
search string Matches name or code.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/products' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 3,
            "name": "Monthly bookkeeping",
            "code": "BOOK-M",
            "unit_price": "1500.00",
            "tax_rate": "15.00",
            "description": "Bookkeeping retainer, billed monthly.",
            "category": {
                "id": 1,
                "name": "Services"
            },
            "created_at": "2026-07-01T09:30:00Z",
            "updated_at": "2026-07-01T09:30:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/products/{product} requires: read

Get a product

Returns a single product by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/products/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 3,
        "name": "Monthly bookkeeping",
        "code": "BOOK-M",
        "unit_price": "1500.00",
        "tax_rate": "15.00",
        "description": "Bookkeeping retainer, billed monthly.",
        "category": {
            "id": 1,
            "name": "Services"
        },
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
POST /v1/products requires: write

Create a product

Creates a product. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

name string required Product or service name.
code string Your internal SKU / code.
category string Category name or id. Unknown names create the category on the fly.
unit_price number required Unit price (≥ 0).
tax_rate number Default tax rate percentage, 0–100.
description string Description (max 1000 characters).

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/products' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Example","unit_price":100}'

Example response 201

{
    "data": {
        "id": 3,
        "name": "Monthly bookkeeping",
        "code": "BOOK-M",
        "unit_price": "1500.00",
        "tax_rate": "15.00",
        "description": "Bookkeeping retainer, billed monthly.",
        "category": {
            "id": 1,
            "name": "Services"
        },
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
PUT /v1/products/{product} requires: write

Update a product

Full update — send every field you want kept, exactly like create.

Body parameters

name string required Product or service name.
code string Your internal SKU / code.
category string Category name or id. Unknown names create the category on the fly.
unit_price number required Unit price (≥ 0).
tax_rate number Default tax rate percentage, 0–100.
description string Description (max 1000 characters).

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/products/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Example","unit_price":100}'

Example response 200

{
    "data": {
        "id": 3,
        "name": "Monthly bookkeeping",
        "code": "BOOK-M",
        "unit_price": "1500.00",
        "tax_rate": "15.00",
        "description": "Bookkeeping retainer, billed monthly.",
        "category": {
            "id": 1,
            "name": "Services"
        },
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
DELETE /v1/products/{product} requires: write

Delete a product

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/products/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/products/{id}/restore requires: write

Restore a product

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/products/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 3,
        "name": "Monthly bookkeeping",
        "code": "BOOK-M",
        "unit_price": "1500.00",
        "tax_rate": "15.00",
        "description": "Bookkeeping retainer, billed monthly.",
        "category": {
            "id": 1,
            "name": "Services"
        },
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}

Invoices

Invoices with server-computed totals. Line totals, per-line tax and the discount are always recalculated from the submitted items — you never send amounts. Requires the `manage_invoices` permission. Creating an invoice counts toward your plan's monthly allowance first, then any extra invoices granted by an admin; hitting the limit returns 403. The list response includes invoice_allowance beside data (remaining monthly slots, extra invoices, reset date).

GET /v1/invoices requires: read

List invoices

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `invoice_date`, `due_date`, `final_amount`, `status`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
search string Matches the invoice number or the client's name.
status integer Filter by status. 0 Draft · 1 Unpaid · 2 Paid · 3 Partially Paid · 4 Overdue
client_id integer Filter by client.
date_from date Invoice date on or after (YYYY-MM-DD).
date_to date Invoice date on or before (YYYY-MM-DD).

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/invoices' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 42,
            "invoice_number": "108",
            "status": 1,
            "status_label": "Unpaid",
            "invoice_date": "2026-07-01",
            "due_date": "2026-07-31",
            "amount": "3000.00",
            "discount_type": 0,
            "discount": "0.00",
            "final_amount": "3450.00",
            "note": null,
            "term": "Payment due within 30 days.",
            "needs_review": false,
            "recurring_frequency": "none",
            "recurring_day": null,
            "recurring_month": null,
            "recurring_status": false,
            "local_currency_amount": null,
            "exchange_rate": null,
            "custom_fields": [
                {
                    "key": "reg_no",
                    "label": "Reg No",
                    "type": "text",
                    "value": "CA 123-456"
                }
            ],
            "client": {
                "id": 7,
                "display_name": "Mokoena Consulting",
                "…": "…"
            },
            "currency": {
                "id": 1,
                "name": "South African Rand",
                "code": "ZAR",
                "symbol": "R",
                "is_custom": false
            },
            "items": [
                {
                    "id": 90,
                    "product_id": 3,
                    "name": "Monthly bookkeeping",
                    "description": null,
                    "quantity": "2.00",
                    "price": "1500.00",
                    "tax_rate": "15.00",
                    "total": "3000.00"
                }
            ],
            "payments": [],
            "paid_amount": "0.00",
            "remaining_amount": "3450.00",
            "created_at": "2026-07-01T09:30:00Z",
            "updated_at": "2026-07-01T09:30:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/invoices/{invoice} requires: read

Get a invoice

Returns a single invoice by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/invoices/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 42,
        "invoice_number": "108",
        "status": 1,
        "status_label": "Unpaid",
        "invoice_date": "2026-07-01",
        "due_date": "2026-07-31",
        "amount": "3000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "3450.00",
        "note": null,
        "term": "Payment due within 30 days.",
        "needs_review": false,
        "recurring_frequency": "none",
        "recurring_day": null,
        "recurring_month": null,
        "recurring_status": false,
        "local_currency_amount": null,
        "exchange_rate": null,
        "custom_fields": [
            {
                "key": "reg_no",
                "label": "Reg No",
                "type": "text",
                "value": "CA 123-456"
            }
        ],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "name": "South African Rand",
            "code": "ZAR",
            "symbol": "R",
            "is_custom": false
        },
        "items": [
            {
                "id": 90,
                "product_id": 3,
                "name": "Monthly bookkeeping",
                "description": null,
                "quantity": "2.00",
                "price": "1500.00",
                "tax_rate": "15.00",
                "total": "3000.00"
            }
        ],
        "payments": [],
        "paid_amount": "0.00",
        "remaining_amount": "3450.00",
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
POST /v1/invoices requires: write

Create a invoice

Creates a invoice. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

invoice_number string Custom invoice number (create only). Omitted or already taken → the next number in your sequence is assigned.
client_id integer required Client id.
currency_id integer required Currency id — must be enabled for your workspace (see GET /currencies).
banking_detail_id integer Banking detail shown on the PDF. Defaults to your workspace default.
invoice_date date required Invoice date (YYYY-MM-DD).
due_date date required Due date — on or after the invoice date.
discount_type integer required Discount type. 0 None · 1 Fixed amount · 2 Percentage
discount number Discount value — an amount when fixed, a percentage when percentage.
status integer required Invoice status. 0 Draft · 1 Unpaid · 2 Paid · 3 Partially Paid · 4 Overdue
note string Note shown on the invoice.
term string Payment terms shown on the invoice.
recurring_frequency string Recurring schedule. On create, a monthly or yearly value spawns a standalone recurring template from this invoice; the template (managed under Recurring Invoices) drives future drafts, and the invoice itself stays a plain document. Ignored on update. none None · monthly Monthly · yearly Yearly
recurring_day integer Day of month (1-31) the spawned template generates on. Only used on create together with recurring_frequency.
recurring_month integer Month (1-12) for yearly recurrence. Only used on create together with recurring_frequency.
recurring_status boolean Legacy flag, ignored: recurring schedules now live on standalone templates. Always false on invoices returned by the API.
local_currency_amount number Amount received in your default currency. Required when marking a foreign-currency invoice as paid; the exchange rate is derived from it.
custom_fields object Values for the workspace's invoice custom fields (Settings → Custom Fields), keyed by field key — e.g. `{"reg_no": "CA 123-456"}`. Each value is validated against the field's type and options; required custom fields must be supplied. Responses return the stored entries as `[{key, label, type, value}]`.
items array required Line items. Totals, tax and the final amount are computed server-side from these rows. Each item: `name` (string, required), `quantity` (number ≥ 0.01, required), `price` (number ≥ 0, required), `tax_rate` (0–100, optional), `description` (optional), `product_id` (optional).

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/invoices' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"client_id":1,"currency_id":1,"invoice_date":"2026-07-01","due_date":"2026-07-01","discount_type":0,"status":0,"items":[{"name":"Line item","quantity":1,"price":100,"tax_rate":15}]}'

Example response 201

{
    "data": {
        "id": 42,
        "invoice_number": "108",
        "status": 1,
        "status_label": "Unpaid",
        "invoice_date": "2026-07-01",
        "due_date": "2026-07-31",
        "amount": "3000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "3450.00",
        "note": null,
        "term": "Payment due within 30 days.",
        "needs_review": false,
        "recurring_frequency": "none",
        "recurring_day": null,
        "recurring_month": null,
        "recurring_status": false,
        "local_currency_amount": null,
        "exchange_rate": null,
        "custom_fields": [
            {
                "key": "reg_no",
                "label": "Reg No",
                "type": "text",
                "value": "CA 123-456"
            }
        ],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "name": "South African Rand",
            "code": "ZAR",
            "symbol": "R",
            "is_custom": false
        },
        "items": [
            {
                "id": 90,
                "product_id": 3,
                "name": "Monthly bookkeeping",
                "description": null,
                "quantity": "2.00",
                "price": "1500.00",
                "tax_rate": "15.00",
                "total": "3000.00"
            }
        ],
        "payments": [],
        "paid_amount": "0.00",
        "remaining_amount": "3450.00",
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
PUT /v1/invoices/{invoice} requires: write

Update a invoice

Full update — send every field you want kept, exactly like create.

Body parameters

client_id integer required Client id.
currency_id integer required Currency id — must be enabled for your workspace (see GET /currencies).
banking_detail_id integer Banking detail shown on the PDF. Defaults to your workspace default.
invoice_date date required Invoice date (YYYY-MM-DD).
due_date date required Due date — on or after the invoice date.
discount_type integer required Discount type. 0 None · 1 Fixed amount · 2 Percentage
discount number Discount value — an amount when fixed, a percentage when percentage.
status integer required Invoice status. 0 Draft · 1 Unpaid · 2 Paid · 3 Partially Paid · 4 Overdue
note string Note shown on the invoice.
term string Payment terms shown on the invoice.
recurring_frequency string Recurring schedule. On create, a monthly or yearly value spawns a standalone recurring template from this invoice; the template (managed under Recurring Invoices) drives future drafts, and the invoice itself stays a plain document. Ignored on update. none None · monthly Monthly · yearly Yearly
recurring_day integer Day of month (1-31) the spawned template generates on. Only used on create together with recurring_frequency.
recurring_month integer Month (1-12) for yearly recurrence. Only used on create together with recurring_frequency.
recurring_status boolean Legacy flag, ignored: recurring schedules now live on standalone templates. Always false on invoices returned by the API.
local_currency_amount number Amount received in your default currency. Required when marking a foreign-currency invoice as paid; the exchange rate is derived from it.
custom_fields object Values for the workspace's invoice custom fields (Settings → Custom Fields), keyed by field key — e.g. `{"reg_no": "CA 123-456"}`. Each value is validated against the field's type and options; required custom fields must be supplied. Responses return the stored entries as `[{key, label, type, value}]`.
items array required Line items. Totals, tax and the final amount are computed server-side from these rows. Each item: `name` (string, required), `quantity` (number ≥ 0.01, required), `price` (number ≥ 0, required), `tax_rate` (0–100, optional), `description` (optional), `product_id` (optional).

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/invoices/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"client_id":1,"currency_id":1,"invoice_date":"2026-07-01","due_date":"2026-07-01","discount_type":0,"status":0,"items":[{"name":"Line item","quantity":1,"price":100,"tax_rate":15}]}'

Example response 200

{
    "data": {
        "id": 42,
        "invoice_number": "108",
        "status": 1,
        "status_label": "Unpaid",
        "invoice_date": "2026-07-01",
        "due_date": "2026-07-31",
        "amount": "3000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "3450.00",
        "note": null,
        "term": "Payment due within 30 days.",
        "needs_review": false,
        "recurring_frequency": "none",
        "recurring_day": null,
        "recurring_month": null,
        "recurring_status": false,
        "local_currency_amount": null,
        "exchange_rate": null,
        "custom_fields": [
            {
                "key": "reg_no",
                "label": "Reg No",
                "type": "text",
                "value": "CA 123-456"
            }
        ],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "name": "South African Rand",
            "code": "ZAR",
            "symbol": "R",
            "is_custom": false
        },
        "items": [
            {
                "id": 90,
                "product_id": 3,
                "name": "Monthly bookkeeping",
                "description": null,
                "quantity": "2.00",
                "price": "1500.00",
                "tax_rate": "15.00",
                "total": "3000.00"
            }
        ],
        "payments": [],
        "paid_amount": "0.00",
        "remaining_amount": "3450.00",
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
DELETE /v1/invoices/{invoice} requires: write

Delete a invoice

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/invoices/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/invoices/{id}/restore requires: write

Restore a invoice

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/invoices/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 42,
        "invoice_number": "108",
        "status": 1,
        "status_label": "Unpaid",
        "invoice_date": "2026-07-01",
        "due_date": "2026-07-31",
        "amount": "3000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "3450.00",
        "note": null,
        "term": "Payment due within 30 days.",
        "needs_review": false,
        "recurring_frequency": "none",
        "recurring_day": null,
        "recurring_month": null,
        "recurring_status": false,
        "local_currency_amount": null,
        "exchange_rate": null,
        "custom_fields": [
            {
                "key": "reg_no",
                "label": "Reg No",
                "type": "text",
                "value": "CA 123-456"
            }
        ],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "name": "South African Rand",
            "code": "ZAR",
            "symbol": "R",
            "is_custom": false
        },
        "items": [
            {
                "id": 90,
                "product_id": 3,
                "name": "Monthly bookkeeping",
                "description": null,
                "quantity": "2.00",
                "price": "1500.00",
                "tax_rate": "15.00",
                "total": "3000.00"
            }
        ],
        "payments": [],
        "paid_amount": "0.00",
        "remaining_amount": "3450.00",
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}

Quotes

Quotes / estimates with the same server-computed totals as invoices. Requires the `manage_quotes` permission.

GET /v1/quotes requires: read

List quotes

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `quote_date`, `due_date`, `final_amount`, `status`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
search string Matches the quote number or the client's name.
status integer Filter by status. 0 Draft · 1 Converted · 2 Pending
client_id integer Filter by client.
date_from date Quote date on or after.
date_to date Quote date on or before.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/quotes' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 17,
            "quote_number": "23",
            "status": 0,
            "status_label": "Draft",
            "quote_date": "2026-07-01",
            "due_date": "2026-07-15",
            "amount": "5000.00",
            "discount_type": 2,
            "discount": "10.00",
            "final_amount": "5250.00",
            "note": null,
            "term": null,
            "custom_fields": [],
            "client": {
                "id": 7,
                "display_name": "Mokoena Consulting",
                "…": "…"
            },
            "currency": {
                "id": 1,
                "code": "ZAR",
                "…": "…"
            },
            "items": [
                {
                    "id": 31,
                    "product_id": null,
                    "name": "Website redesign",
                    "description": null,
                    "quantity": "1.00",
                    "price": "5000.00",
                    "tax_rate": "15.00",
                    "total": "5000.00"
                }
            ],
            "created_at": "2026-07-01T09:30:00Z",
            "updated_at": "2026-07-01T09:30:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/quotes/{quote} requires: read

Get a quote

Returns a single quote by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/quotes/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 17,
        "quote_number": "23",
        "status": 0,
        "status_label": "Draft",
        "quote_date": "2026-07-01",
        "due_date": "2026-07-15",
        "amount": "5000.00",
        "discount_type": 2,
        "discount": "10.00",
        "final_amount": "5250.00",
        "note": null,
        "term": null,
        "custom_fields": [],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "items": [
            {
                "id": 31,
                "product_id": null,
                "name": "Website redesign",
                "description": null,
                "quantity": "1.00",
                "price": "5000.00",
                "tax_rate": "15.00",
                "total": "5000.00"
            }
        ],
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
POST /v1/quotes requires: write

Create a quote

Creates a quote. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

quote_number string Custom quote number. Omitted on create (or already taken) → the next number in your sequence is assigned. On update, omitted keeps the current number.
client_id integer required Client id.
currency_id integer required Currency id — must be enabled for your workspace.
banking_detail_id integer Banking detail shown on the PDF. Defaults to your workspace default.
quote_date date required Quote date (YYYY-MM-DD).
due_date date required Valid-until date — on or after the quote date.
discount_type integer required Discount type. 0 None · 1 Fixed amount · 2 Percentage
discount number Discount value.
status integer required Quote status. 0 Draft · 1 Converted · 2 Pending
note string Note shown on the quote.
term string Terms shown on the quote.
custom_fields object Values for the workspace's quote custom fields (Settings → Custom Fields), keyed by field key — e.g. `{"reg_no": "CA 123-456"}`. Quote fields are managed separately from invoice fields. Each value is validated against the field's type and options; required custom fields must be supplied. Responses return the stored entries as `[{key, label, type, value}]`.
items array required Line items. Totals, tax and the final amount are computed server-side from these rows. Each item: `name` (string, required), `quantity` (number ≥ 0.01, required), `price` (number ≥ 0, required), `tax_rate` (0–100, optional), `description` (optional), `product_id` (optional).

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/quotes' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"client_id":1,"currency_id":1,"quote_date":"2026-07-01","due_date":"2026-07-01","discount_type":0,"status":0,"items":[{"name":"Line item","quantity":1,"price":100,"tax_rate":15}]}'

Example response 201

{
    "data": {
        "id": 17,
        "quote_number": "23",
        "status": 0,
        "status_label": "Draft",
        "quote_date": "2026-07-01",
        "due_date": "2026-07-15",
        "amount": "5000.00",
        "discount_type": 2,
        "discount": "10.00",
        "final_amount": "5250.00",
        "note": null,
        "term": null,
        "custom_fields": [],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "items": [
            {
                "id": 31,
                "product_id": null,
                "name": "Website redesign",
                "description": null,
                "quantity": "1.00",
                "price": "5000.00",
                "tax_rate": "15.00",
                "total": "5000.00"
            }
        ],
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
PUT /v1/quotes/{quote} requires: write

Update a quote

Full update — send every field you want kept, exactly like create.

Body parameters

quote_number string Custom quote number. Omitted on create (or already taken) → the next number in your sequence is assigned. On update, omitted keeps the current number.
client_id integer required Client id.
currency_id integer required Currency id — must be enabled for your workspace.
banking_detail_id integer Banking detail shown on the PDF. Defaults to your workspace default.
quote_date date required Quote date (YYYY-MM-DD).
due_date date required Valid-until date — on or after the quote date.
discount_type integer required Discount type. 0 None · 1 Fixed amount · 2 Percentage
discount number Discount value.
status integer required Quote status. 0 Draft · 1 Converted · 2 Pending
note string Note shown on the quote.
term string Terms shown on the quote.
custom_fields object Values for the workspace's quote custom fields (Settings → Custom Fields), keyed by field key — e.g. `{"reg_no": "CA 123-456"}`. Quote fields are managed separately from invoice fields. Each value is validated against the field's type and options; required custom fields must be supplied. Responses return the stored entries as `[{key, label, type, value}]`.
items array required Line items. Totals, tax and the final amount are computed server-side from these rows. Each item: `name` (string, required), `quantity` (number ≥ 0.01, required), `price` (number ≥ 0, required), `tax_rate` (0–100, optional), `description` (optional), `product_id` (optional).

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/quotes/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"client_id":1,"currency_id":1,"quote_date":"2026-07-01","due_date":"2026-07-01","discount_type":0,"status":0,"items":[{"name":"Line item","quantity":1,"price":100,"tax_rate":15}]}'

Example response 200

{
    "data": {
        "id": 17,
        "quote_number": "23",
        "status": 0,
        "status_label": "Draft",
        "quote_date": "2026-07-01",
        "due_date": "2026-07-15",
        "amount": "5000.00",
        "discount_type": 2,
        "discount": "10.00",
        "final_amount": "5250.00",
        "note": null,
        "term": null,
        "custom_fields": [],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "items": [
            {
                "id": 31,
                "product_id": null,
                "name": "Website redesign",
                "description": null,
                "quantity": "1.00",
                "price": "5000.00",
                "tax_rate": "15.00",
                "total": "5000.00"
            }
        ],
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}
DELETE /v1/quotes/{quote} requires: write

Delete a quote

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/quotes/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/quotes/{id}/restore requires: write

Restore a quote

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/quotes/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 17,
        "quote_number": "23",
        "status": 0,
        "status_label": "Draft",
        "quote_date": "2026-07-01",
        "due_date": "2026-07-15",
        "amount": "5000.00",
        "discount_type": 2,
        "discount": "10.00",
        "final_amount": "5250.00",
        "note": null,
        "term": null,
        "custom_fields": [],
        "client": {
            "id": 7,
            "display_name": "Mokoena Consulting",
            "…": "…"
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "items": [
            {
                "id": 31,
                "product_id": null,
                "name": "Website redesign",
                "description": null,
                "quantity": "1.00",
                "price": "5000.00",
                "tax_rate": "15.00",
                "total": "5000.00"
            }
        ],
        "created_at": "2026-07-01T09:30:00Z",
        "updated_at": "2026-07-01T09:30:00Z",
        "deleted_at": null
    }
}

Purchase Orders

Purchase orders sent to suppliers, with the same server-computed totals as invoices. Requires the `manage_expenses` permission.

GET /v1/purchase-orders requires: read

List purchase-orders

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `order_date`, `expected_date`, `final_amount`, `status`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
search string Matches the PO number, supplier name or reference.
status integer Filter by status. 0 Draft · 1 Sent · 2 Confirmed · 3 Received · 4 Billed · 5 Cancelled
date_from date Order date on or after.
date_to date Order date on or before.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/purchase-orders' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 5,
            "po_number": "4",
            "status": 1,
            "status_label": "Sent",
            "supplier_name": "Acme Supplies",
            "supplier_email": "orders@acme.example",
            "supplier_phone": null,
            "supplier_vat_number": null,
            "supplier_address": null,
            "delivery_address": null,
            "reference": "Stationery Q3",
            "order_date": "2026-07-10",
            "expected_date": "2026-07-24",
            "amount": "1000.00",
            "discount_type": 0,
            "discount": "0.00",
            "final_amount": "1150.00",
            "note": null,
            "term": null,
            "expense_id": null,
            "sent_at": "2026-07-10T11:00:00Z",
            "currency": {
                "id": 2,
                "code": "NAD",
                "…": "…"
            },
            "items": [
                {
                    "id": 11,
                    "product_id": null,
                    "name": "Printer paper",
                    "description": null,
                    "quantity": "10.00",
                    "price": "50.00",
                    "tax_rate": "15.00",
                    "total": "500.00"
                }
            ],
            "created_at": "2026-07-10T09:30:00Z",
            "updated_at": "2026-07-10T11:00:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/purchase-orders/{purchase_order} requires: read

Get a purchase order

Returns a single purchase order by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/purchase-orders/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 5,
        "po_number": "4",
        "status": 1,
        "status_label": "Sent",
        "supplier_name": "Acme Supplies",
        "supplier_email": "orders@acme.example",
        "supplier_phone": null,
        "supplier_vat_number": null,
        "supplier_address": null,
        "delivery_address": null,
        "reference": "Stationery Q3",
        "order_date": "2026-07-10",
        "expected_date": "2026-07-24",
        "amount": "1000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "1150.00",
        "note": null,
        "term": null,
        "expense_id": null,
        "sent_at": "2026-07-10T11:00:00Z",
        "currency": {
            "id": 2,
            "code": "NAD",
            "…": "…"
        },
        "items": [
            {
                "id": 11,
                "product_id": null,
                "name": "Printer paper",
                "description": null,
                "quantity": "10.00",
                "price": "50.00",
                "tax_rate": "15.00",
                "total": "500.00"
            }
        ],
        "created_at": "2026-07-10T09:30:00Z",
        "updated_at": "2026-07-10T11:00:00Z",
        "deleted_at": null
    }
}
POST /v1/purchase-orders requires: write

Create a purchase order

Creates a purchase order. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

supplier_name string required Supplier name.
supplier_email string Supplier email.
supplier_phone string Supplier phone.
supplier_vat_number string Supplier VAT number.
supplier_address string Supplier address.
delivery_address string Delivery address.
reference string Your reference for this order.
currency_id integer required Currency id — must be enabled for your workspace.
order_date date required Order date (YYYY-MM-DD).
expected_date date Expected delivery date — on or after the order date.
discount_type integer required Discount type. 0 None · 1 Fixed amount · 2 Percentage
discount number Discount value.
status integer required Order status. Billed orders are managed by converting to an expense in the app. 0 Draft · 1 Sent · 2 Confirmed · 3 Received · 5 Cancelled
note string Note shown on the order.
term string Terms shown on the order.
items array required Line items. Totals, tax and the final amount are computed server-side from these rows. Each item: `name` (string, required), `quantity` (number ≥ 0.01, required), `price` (number ≥ 0, required), `tax_rate` (0–100, optional), `description` (optional), `product_id` (optional).

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/purchase-orders' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"supplier_name":"Example","currency_id":1,"order_date":"2026-07-01","discount_type":0,"status":0,"items":[{"name":"Line item","quantity":1,"price":100,"tax_rate":15}]}'

Example response 201

{
    "data": {
        "id": 5,
        "po_number": "4",
        "status": 1,
        "status_label": "Sent",
        "supplier_name": "Acme Supplies",
        "supplier_email": "orders@acme.example",
        "supplier_phone": null,
        "supplier_vat_number": null,
        "supplier_address": null,
        "delivery_address": null,
        "reference": "Stationery Q3",
        "order_date": "2026-07-10",
        "expected_date": "2026-07-24",
        "amount": "1000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "1150.00",
        "note": null,
        "term": null,
        "expense_id": null,
        "sent_at": "2026-07-10T11:00:00Z",
        "currency": {
            "id": 2,
            "code": "NAD",
            "…": "…"
        },
        "items": [
            {
                "id": 11,
                "product_id": null,
                "name": "Printer paper",
                "description": null,
                "quantity": "10.00",
                "price": "50.00",
                "tax_rate": "15.00",
                "total": "500.00"
            }
        ],
        "created_at": "2026-07-10T09:30:00Z",
        "updated_at": "2026-07-10T11:00:00Z",
        "deleted_at": null
    }
}
PUT /v1/purchase-orders/{purchase_order} requires: write

Update a purchase order

Full update — send every field you want kept, exactly like create.

Body parameters

supplier_name string required Supplier name.
supplier_email string Supplier email.
supplier_phone string Supplier phone.
supplier_vat_number string Supplier VAT number.
supplier_address string Supplier address.
delivery_address string Delivery address.
reference string Your reference for this order.
currency_id integer required Currency id — must be enabled for your workspace.
order_date date required Order date (YYYY-MM-DD).
expected_date date Expected delivery date — on or after the order date.
discount_type integer required Discount type. 0 None · 1 Fixed amount · 2 Percentage
discount number Discount value.
status integer required Order status. Billed orders are managed by converting to an expense in the app. 0 Draft · 1 Sent · 2 Confirmed · 3 Received · 5 Cancelled
note string Note shown on the order.
term string Terms shown on the order.
items array required Line items. Totals, tax and the final amount are computed server-side from these rows. Each item: `name` (string, required), `quantity` (number ≥ 0.01, required), `price` (number ≥ 0, required), `tax_rate` (0–100, optional), `description` (optional), `product_id` (optional).

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/purchase-orders/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"supplier_name":"Example","currency_id":1,"order_date":"2026-07-01","discount_type":0,"status":0,"items":[{"name":"Line item","quantity":1,"price":100,"tax_rate":15}]}'

Example response 200

{
    "data": {
        "id": 5,
        "po_number": "4",
        "status": 1,
        "status_label": "Sent",
        "supplier_name": "Acme Supplies",
        "supplier_email": "orders@acme.example",
        "supplier_phone": null,
        "supplier_vat_number": null,
        "supplier_address": null,
        "delivery_address": null,
        "reference": "Stationery Q3",
        "order_date": "2026-07-10",
        "expected_date": "2026-07-24",
        "amount": "1000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "1150.00",
        "note": null,
        "term": null,
        "expense_id": null,
        "sent_at": "2026-07-10T11:00:00Z",
        "currency": {
            "id": 2,
            "code": "NAD",
            "…": "…"
        },
        "items": [
            {
                "id": 11,
                "product_id": null,
                "name": "Printer paper",
                "description": null,
                "quantity": "10.00",
                "price": "50.00",
                "tax_rate": "15.00",
                "total": "500.00"
            }
        ],
        "created_at": "2026-07-10T09:30:00Z",
        "updated_at": "2026-07-10T11:00:00Z",
        "deleted_at": null
    }
}
DELETE /v1/purchase-orders/{purchase_order} requires: write

Delete a purchase order

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/purchase-orders/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/purchase-orders/{id}/restore requires: write

Restore a purchase order

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/purchase-orders/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 5,
        "po_number": "4",
        "status": 1,
        "status_label": "Sent",
        "supplier_name": "Acme Supplies",
        "supplier_email": "orders@acme.example",
        "supplier_phone": null,
        "supplier_vat_number": null,
        "supplier_address": null,
        "delivery_address": null,
        "reference": "Stationery Q3",
        "order_date": "2026-07-10",
        "expected_date": "2026-07-24",
        "amount": "1000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "1150.00",
        "note": null,
        "term": null,
        "expense_id": null,
        "sent_at": "2026-07-10T11:00:00Z",
        "currency": {
            "id": 2,
            "code": "NAD",
            "…": "…"
        },
        "items": [
            {
                "id": 11,
                "product_id": null,
                "name": "Printer paper",
                "description": null,
                "quantity": "10.00",
                "price": "50.00",
                "tax_rate": "15.00",
                "total": "500.00"
            }
        ],
        "created_at": "2026-07-10T09:30:00Z",
        "updated_at": "2026-07-10T11:00:00Z",
        "deleted_at": null
    }
}
POST /v1/purchase-orders/{purchase_order}/status requires: write

Transition status

Moves the order through its lifecycle (draft → sent → confirmed → received / cancelled). Marking as sent stamps sent_at once. Billed orders can no longer change (422).

Body parameters

status integer required The new status. 0 Draft · 1 Sent · 2 Confirmed · 3 Received · 5 Cancelled

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/purchase-orders/42/status' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"status":0}'

Example response 200

{
    "data": {
        "id": 5,
        "po_number": "4",
        "status": 1,
        "status_label": "Sent",
        "supplier_name": "Acme Supplies",
        "supplier_email": "orders@acme.example",
        "supplier_phone": null,
        "supplier_vat_number": null,
        "supplier_address": null,
        "delivery_address": null,
        "reference": "Stationery Q3",
        "order_date": "2026-07-10",
        "expected_date": "2026-07-24",
        "amount": "1000.00",
        "discount_type": 0,
        "discount": "0.00",
        "final_amount": "1150.00",
        "note": null,
        "term": null,
        "expense_id": null,
        "sent_at": "2026-07-10T11:00:00Z",
        "currency": {
            "id": 2,
            "code": "NAD",
            "…": "…"
        },
        "items": [
            {
                "id": 11,
                "product_id": null,
                "name": "Printer paper",
                "description": null,
                "quantity": "10.00",
                "price": "50.00",
                "tax_rate": "15.00",
                "total": "500.00"
            }
        ],
        "created_at": "2026-07-10T09:30:00Z",
        "updated_at": "2026-07-10T11:00:00Z",
        "deleted_at": null
    }
}

Payments

Payments recorded against invoices. New payments start unapproved; approving one recalculates the invoice's paid / partially-paid / overdue status. Requires the `manage_payments` permission.

GET /v1/payments requires: read

List payments

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `payment_date`, `amount`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
invoice_id integer Filter by invoice.
is_approved boolean Filter by approval state.
date_from date Payment date on or after.
date_to date Payment date on or before.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/payments' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 88,
            "invoice_id": 42,
            "amount": "3450.00",
            "payment_mode": "bank_transfer",
            "payment_date": "2026-07-15",
            "notes": "EFT ref 99123",
            "is_approved": false,
            "invoice": {
                "id": 42,
                "invoice_number": "108"
            },
            "created_at": "2026-07-15T14:00:00Z",
            "updated_at": "2026-07-15T14:00:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/payments/{payment} requires: read

Get a payment

Returns a single payment by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/payments/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 88,
        "invoice_id": 42,
        "amount": "3450.00",
        "payment_mode": "bank_transfer",
        "payment_date": "2026-07-15",
        "notes": "EFT ref 99123",
        "is_approved": false,
        "invoice": {
            "id": 42,
            "invoice_number": "108"
        },
        "created_at": "2026-07-15T14:00:00Z",
        "updated_at": "2026-07-15T14:00:00Z",
        "deleted_at": null
    }
}
POST /v1/payments requires: write

Create a payment

Creates a payment. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

invoice_id integer required The invoice this payment is against.
amount number required Payment amount — may not exceed the invoice's remaining balance.
payment_mode string required How the payment was made. cash Cash · bank_transfer Bank transfer · card Card · cheque Cheque · other Other
payment_date date required Payment date (YYYY-MM-DD).
notes string Note (max 1000 characters).

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/payments' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"invoice_id":1,"amount":100,"payment_mode":"cash","payment_date":"2026-07-01"}'

Example response 201

{
    "data": {
        "id": 88,
        "invoice_id": 42,
        "amount": "3450.00",
        "payment_mode": "bank_transfer",
        "payment_date": "2026-07-15",
        "notes": "EFT ref 99123",
        "is_approved": false,
        "invoice": {
            "id": 42,
            "invoice_number": "108"
        },
        "created_at": "2026-07-15T14:00:00Z",
        "updated_at": "2026-07-15T14:00:00Z",
        "deleted_at": null
    }
}
PUT /v1/payments/{payment} requires: write

Update a payment

Full update — send every field you want kept, exactly like create.

Body parameters

invoice_id integer required The invoice this payment is against.
amount number required Payment amount — may not exceed the invoice's remaining balance.
payment_mode string required How the payment was made. cash Cash · bank_transfer Bank transfer · card Card · cheque Cheque · other Other
payment_date date required Payment date (YYYY-MM-DD).
notes string Note (max 1000 characters).

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/payments/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"invoice_id":1,"amount":100,"payment_mode":"cash","payment_date":"2026-07-01"}'

Example response 200

{
    "data": {
        "id": 88,
        "invoice_id": 42,
        "amount": "3450.00",
        "payment_mode": "bank_transfer",
        "payment_date": "2026-07-15",
        "notes": "EFT ref 99123",
        "is_approved": false,
        "invoice": {
            "id": 42,
            "invoice_number": "108"
        },
        "created_at": "2026-07-15T14:00:00Z",
        "updated_at": "2026-07-15T14:00:00Z",
        "deleted_at": null
    }
}
DELETE /v1/payments/{payment} requires: write

Delete a payment

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/payments/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/payments/{id}/restore requires: write

Restore a payment

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/payments/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 88,
        "invoice_id": 42,
        "amount": "3450.00",
        "payment_mode": "bank_transfer",
        "payment_date": "2026-07-15",
        "notes": "EFT ref 99123",
        "is_approved": false,
        "invoice": {
            "id": 42,
            "invoice_number": "108"
        },
        "created_at": "2026-07-15T14:00:00Z",
        "updated_at": "2026-07-15T14:00:00Z",
        "deleted_at": null
    }
}
POST /v1/payments/{payment}/approve requires: write

Approve a payment

Marks the payment as approved and recalculates the invoice status.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/payments/42/approve' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 88,
        "invoice_id": 42,
        "amount": "3450.00",
        "payment_mode": "bank_transfer",
        "payment_date": "2026-07-15",
        "notes": "EFT ref 99123",
        "is_approved": true,
        "invoice": {
            "id": 42,
            "invoice_number": "108"
        },
        "created_at": "2026-07-15T14:00:00Z",
        "updated_at": "2026-07-15T14:00:00Z",
        "deleted_at": null
    }
}
POST /v1/payments/{payment}/reject requires: write

Reject a payment

Marks the payment as not approved and recalculates the invoice status.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/payments/42/reject' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 88,
        "invoice_id": 42,
        "amount": "3450.00",
        "payment_mode": "bank_transfer",
        "payment_date": "2026-07-15",
        "notes": "EFT ref 99123",
        "is_approved": false,
        "invoice": {
            "id": 42,
            "invoice_number": "108"
        },
        "created_at": "2026-07-15T14:00:00Z",
        "updated_at": "2026-07-15T14:00:00Z",
        "deleted_at": null
    }
}

Expenses

Business expenses. A single scanned expense response includes its extracted invoice items and each item's category; when no items exist, the expense-level category applies to the whole invoice. API-created expenses are Manual-source; scanned invoices and bank-statement imports are created in the app. Requires the `manage_expenses` permission.

GET /v1/expenses requires: read

List expenses

Paginated list, newest first by default.

Query parameters

page integer Page number (default 1).
per_page integer Results per page, 1–100 (default 25).
sort string Sort key. One of: `expense_date`, `total_amount`, `vendor_name`, `created_at`.
direction string `asc` or `desc` (default `desc`).
trashed string Soft-delete visibility: `with` includes deleted records, `only` returns deleted records exclusively.
search string Matches vendor name, description or reference.
status integer Filter by status. 0 Draft · 1 Pending Review · 2 Approved · 3 Rejected
source integer Filter by source. 0 Manual · 1 Invoice Scan · 2 Bank Statement
expense_category_id integer Filter by whole-expense or invoice-item category.
date_from date Expense date on or after.
date_to date Expense date on or before.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/expenses' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 231,
            "expense_number": "58",
            "vendor_name": "CTM Office Park",
            "vendor_tax_number": null,
            "amount": "2000.00",
            "tax_amount": "300.00",
            "total_amount": "2300.00",
            "expense_date": "2026-07-03",
            "description": "July office rent",
            "reference": "RENT-JUL",
            "source": 0,
            "source_label": "Manual",
            "status": 2,
            "status_label": "Approved",
            "has_invoice": false,
            "invoice_required": true,
            "category": {
                "id": 4,
                "name": "Rent",
                "color": "#f97316",
                "is_active": true
            },
            "currency": {
                "id": 1,
                "code": "ZAR",
                "…": "…"
            },
            "created_at": "2026-07-03T08:00:00Z",
            "updated_at": "2026-07-03T08:00:00Z",
            "deleted_at": null
        }
    ],
    "links": {
        "first": "…",
        "last": "…",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 1,
        "…": "…"
    }
}
GET /v1/expenses/{expense} requires: read

Get a expense

Returns a single expense by id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/expenses/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 231,
        "expense_number": "58",
        "vendor_name": "CTM Office Park",
        "vendor_tax_number": null,
        "amount": "2000.00",
        "tax_amount": "300.00",
        "total_amount": "2300.00",
        "expense_date": "2026-07-03",
        "description": "July office rent",
        "reference": "RENT-JUL",
        "source": 0,
        "source_label": "Manual",
        "status": 2,
        "status_label": "Approved",
        "has_invoice": false,
        "invoice_required": true,
        "category": {
            "id": 4,
            "name": "Rent",
            "color": "#f97316",
            "is_active": true
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "created_at": "2026-07-03T08:00:00Z",
        "updated_at": "2026-07-03T08:00:00Z",
        "deleted_at": null
    }
}
POST /v1/expenses requires: write

Create a expense

Creates a expense. Returns 201 with the created record; validation failures return 422 with per-field errors.

Body parameters

vendor_name string Who was paid.
vendor_tax_number string Vendor VAT / tax number.
amount number required Amount excluding tax.
tax_amount number required Tax portion (0 if none).
total_amount number required Total including tax.
expense_date date required Expense date (YYYY-MM-DD).
description string What the expense was for.
reference string Receipt / statement reference.
expense_category_id integer Category id (see GET /expense-categories).
currency_id integer Currency id.
invoice_required boolean Whether a supplier invoice must still be attached.
status integer Status — defaults to Pending Review on create. 0 Draft · 1 Pending Review · 2 Approved · 3 Rejected

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/expenses' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"amount":100,"tax_amount":100,"total_amount":100,"expense_date":"2026-07-01"}'

Example response 201

{
    "data": {
        "id": 231,
        "expense_number": "58",
        "vendor_name": "CTM Office Park",
        "vendor_tax_number": null,
        "amount": "2000.00",
        "tax_amount": "300.00",
        "total_amount": "2300.00",
        "expense_date": "2026-07-03",
        "description": "July office rent",
        "reference": "RENT-JUL",
        "source": 0,
        "source_label": "Manual",
        "status": 2,
        "status_label": "Approved",
        "has_invoice": false,
        "invoice_required": true,
        "category": {
            "id": 4,
            "name": "Rent",
            "color": "#f97316",
            "is_active": true
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "created_at": "2026-07-03T08:00:00Z",
        "updated_at": "2026-07-03T08:00:00Z",
        "deleted_at": null
    }
}
PUT /v1/expenses/{expense} requires: write

Update a expense

Full update — send every field you want kept, exactly like create.

Body parameters

vendor_name string Who was paid.
vendor_tax_number string Vendor VAT / tax number.
amount number required Amount excluding tax.
tax_amount number required Tax portion (0 if none).
total_amount number required Total including tax.
expense_date date required Expense date (YYYY-MM-DD).
description string What the expense was for.
reference string Receipt / statement reference.
expense_category_id integer Category id (see GET /expense-categories).
currency_id integer Currency id.
invoice_required boolean Whether a supplier invoice must still be attached.
status integer Status — defaults to Pending Review on create. 0 Draft · 1 Pending Review · 2 Approved · 3 Rejected

Example request

curl -X PUT 'https://lekkeraccounting.com/api/v1/expenses/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"amount":100,"tax_amount":100,"total_amount":100,"expense_date":"2026-07-01"}'

Example response 200

{
    "data": {
        "id": 231,
        "expense_number": "58",
        "vendor_name": "CTM Office Park",
        "vendor_tax_number": null,
        "amount": "2000.00",
        "tax_amount": "300.00",
        "total_amount": "2300.00",
        "expense_date": "2026-07-03",
        "description": "July office rent",
        "reference": "RENT-JUL",
        "source": 0,
        "source_label": "Manual",
        "status": 2,
        "status_label": "Approved",
        "has_invoice": false,
        "invoice_required": true,
        "category": {
            "id": 4,
            "name": "Rent",
            "color": "#f97316",
            "is_active": true
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "created_at": "2026-07-03T08:00:00Z",
        "updated_at": "2026-07-03T08:00:00Z",
        "deleted_at": null
    }
}
DELETE /v1/expenses/{expense} requires: write

Delete a expense

Soft delete — the record moves to the trash and can be restored. Returns 204 with no body.

Example request

curl -X DELETE 'https://lekkeraccounting.com/api/v1/expenses/42' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 204

No body.

POST /v1/expenses/{id}/restore requires: write

Restore a expense

Restores a soft-deleted record from the trash. 404 if the id is not currently deleted.

Example request

curl -X POST 'https://lekkeraccounting.com/api/v1/expenses/42/restore' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": {
        "id": 231,
        "expense_number": "58",
        "vendor_name": "CTM Office Park",
        "vendor_tax_number": null,
        "amount": "2000.00",
        "tax_amount": "300.00",
        "total_amount": "2300.00",
        "expense_date": "2026-07-03",
        "description": "July office rent",
        "reference": "RENT-JUL",
        "source": 0,
        "source_label": "Manual",
        "status": 2,
        "status_label": "Approved",
        "has_invoice": false,
        "invoice_required": true,
        "category": {
            "id": 4,
            "name": "Rent",
            "color": "#f97316",
            "is_active": true
        },
        "currency": {
            "id": 1,
            "code": "ZAR",
            "…": "…"
        },
        "created_at": "2026-07-03T08:00:00Z",
        "updated_at": "2026-07-03T08:00:00Z",
        "deleted_at": null
    }
}

Reference Data

Read-only lookups for the ids used elsewhere in the API.

GET /v1/currencies requires: read

List currencies

Currencies enabled for your workspace — valid values for every currency_id field.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/currencies' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 1,
            "name": "South African Rand",
            "code": "ZAR",
            "symbol": "R",
            "is_custom": false
        },
        {
            "id": 2,
            "name": "Namibian Dollar",
            "code": "NAD",
            "symbol": "N$",
            "is_custom": false
        }
    ]
}
GET /v1/expense-categories requires: read

List expense categories

Active expense categories — valid values for expense_category_id.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/expense-categories' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 4,
            "name": "Rent",
            "color": "#f97316",
            "is_active": true
        }
    ]
}
GET /v1/banking-details requires: read

List banking details

Your workspace's bank accounts — valid values for banking_detail_id on invoices and quotes. Requires the `manage_invoices` permission.

Example request

curl -X GET 'https://lekkeraccounting.com/api/v1/banking-details' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Accept: application/json'

Example response 200

{
    "data": [
        {
            "id": 1,
            "name": "FNB Cheque",
            "bank_name": "FNB",
            "account_holder": "Mokoena Consulting",
            "account_number": "62012345678",
            "branch_code": "250655",
            "swift_code": null,
            "iban": null,
            "is_default": true
        }
    ]
}