> ## Documentation Index
> Fetch the complete documentation index at: https://docs.onboardme.app/llms.txt
> Use this file to discover all available pages before exploring further.

# List Xero tax rates

> Returns selected practice tax rates for Xero invoice lines.

Use `extTaxID` on create line items (practice tax option id). Empty when Xero is not connected.

**429:** Rate limit exceeded.



## OpenAPI

````yaml GET /api/v1/bills/ledger/tax-rates
openapi: 3.0.4
info:
  title: OnboardMe External API
  description: "Partner integration API for OnboardMe. All routes are under `/api/v1/…` on your **regional** API host.\r\n\r\n---\r\n\r\n## Regional base URLs\r\n\r\nUse the host that matches where the practice is hosted (a practice is tied to one region).\r\n\r\n| Region | Base URL |\r\n| ------ | -------- |\r\n| Australia & New Zealand | `https://anzapi.onboardme.app` |\r\n| United Kingdom | `https://ukapi.onboardme.app` |\r\n| South Africa | `https://zaapi.onboardme.app` |\r\n\r\nIn your HTTP client, pick the matching **Server** / base URL (or set `baseUrl` to one of the URLs above).\r\n\r\nExample: `GET https://ukapi.onboardme.app/api/v1/auth/validate`\r\n\r\n---\r\n\r\n## Authentication\r\n\r\n**Production integrations (recommended):** send both headers on every request:\r\n\r\n- **`X-OM-Auth-ID`** — Client ID (GUID from OnboardMe practice settings)\r\n- **`X-OM-Auth-Key`** — Client secret paired with that Client ID\r\n\r\n**Postman / Swagger Try it out:** use **Authorize** (HTTP Basic) with **Client ID** as the username and **Client secret** as the password. In Postman, set Basic Auth **once on the collection** so every request inherits it — you do not need auth on each request or two separate header fields.\r\n\r\nHTTPS only. Credentials are scoped to one practice; this API does not use end-user passwords.\r\n\r\n---\r\n\r\n## Partner onboarding\r\n\r\n1. Obtain **Client ID** and **secret** from the practice administrator (OnboardMe).\r\n2. Select the **correct regional base URL** (ANZ, UK, or ZA); practices do not span regions.\r\n3. Call `GET /api/v1/auth/validate` to confirm access and read `canWrite` before using any write endpoint.\r\n4. Run an initial **full** import, then use `lastUpdated` on list endpoints for **incremental** sync.\r\n5. On **429 Too Many Requests**, honour `Retry-After` and backoff; avoid polling faster than you need.\r\n\r\n---\r\n\r\n## Pagination and sync\r\n\r\nList endpoints support optional `pageNumber` (1-based, up to 10000) and `lastUpdated` (UTC). Use `lastUpdated` for incremental sync: only rows changed on or after that instant. `lastUpdated` must not be in the future.\r\n\r\n---\r\n\r\n## Rate limits\r\n\r\nPer Client ID (default: 60/minute, 1000/hour, 10000/day). **429** responses include JSON details and headers: `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.\r\n\r\n---\r\n\r\n## Write access\r\n\r\nCreating or updating data requires `canWrite: true` from `/api/v1/auth/validate`. Read-only clients get **403** on write routes.\r\n\r\nLead endpoints under **`/api/v1/leads`** support listing, detail, notes, create, and **convert-to-client** (promote a lead to an Active client).\r\n\r\n**Contacts** (`/api/v1/contacts/list` and `/api/v1/entities/{entityKey}/contacts`) expose people linked to clients — list, create, and update.\r\n\r\n**ID verifications** — **`POST /api/v1/id-verifications/send`** starts identity verification (optionally with AML via **`includeAml`**). **`POST /api/v1/id-verifications/aml-checks`** runs a standalone **standard** or **extensive** AML screening on an existing client (`entityKey`). Download a verification certificate PDF with **`GET /api/v1/id-verifications/entities/{entityKey}/certificate`**.\r\n\r\n**Onboarding documents** — **`GET /api/v1/onboardings/{key}/documents`** lists requirements per client entity (`isUploaded` / `isBypassed`). Download one file via a short-lived signed S3 URL: **`GET …/entities/{entityOnboardingId}/documents/{documentId}`**. Download all uploaded files as a zip: **`GET …/entities/{entityOnboardingId}/documents/zip`**.\r\n\r\n**Bills** — **`GET /api/v1/bills/list`** lists invoices by **bill date** range (`startDate` / `endDate`). **`GET /api/v1/bills/{billingId}`** returns detail including ledger fields (`externalId`, `externalNumber`, `externalUrl`). **`GET /api/v1/bills/{billingId}/pdf`** downloads the OnboardMe invoice PDF. **`POST /api/v1/bills`** creates an **adhoc** bill for an Active client entity only (no engagement / onboarding entity link; requires `canWrite`; does **not** push to Xero — stores `sendBill` / `syncNow`). When Xero is connected, lines need a revenue account (`extAccountID`) or sale item plus tax (`extTaxID`); use **`GET /api/v1/bills/ledger/status|accounts|tax-rates|branding-themes`**.\r\n\r\n**Proposals** — List a client's proposals with **`GET /api/v1/proposals/list?entityKey=`**. Load previous **services, invoices, recurring billings, and jobs** via **`GET /api/v1/proposals/{key}/pricing`** (or full **`GET /api/v1/proposals/{key}`**). **`POST /api/v1/proposals/{key}/duplicate`** copies pricing, billings, and jobs into a new draft. **`POST /api/v1/proposals/{key}/renew`** does the same and marks the original Renewed (**`GET …/renew-preview`** first for signatory choice). **`POST /api/v1/proposals/from-template`** creates from a saved template. **`POST /api/v1/proposals`** creates an **adhoc** proposal with caller-supplied **services**, optional **XPM jobs**, **FYI jobs**, and **billings**. Catalogs: **`GET /api/v1/proposals/services`**, **`…/xpm-job-templates`**, **`…/xpm-job-categories`**, **`…/xpm-staff`**, **`…/fyi-job-templates`**, **`…/fyi-job-states`**.\r\n\r\n**Recurring billing** — **`GET /api/v1/recurring-billing/list`**, **`GET /api/v1/recurring-billing/{recurringBillingId}`**, and **`GET …/runs`** for schedule list, detail, and generated-run history. Ledger invoice numbers/URLs appear on generated bills, not on the schedule itself.\r\n\r\n---\r\n\r\n## Outbound webhooks\r\n\r\nRegister a subscriber URL with **`POST /api/v1/webhooks`** (requires `canWrite`). Supported event keys are listed at **`GET /api/v1/webhooks/events`** (e.g. `proposal.accepted`, `eform.submitted`). One subscription per event; repeat subscribe with the same URL for multiple events.\r\n\r\n**Subscribe response (`201`):** `{ \"id\", \"webhookSecret\" }` — store `webhookSecret` immediately; it is **not** returned again on list/load.\r\n\r\n**Deliveries:** OnboardMe `POST`s JSON to your URL. Each request includes:\r\n\r\n| Header | Description |\r\n| ------ | ------------- |\r\n| `X-Om-Event` | Event key (e.g. `proposal.accepted`) |\r\n| `X-Om-Event-Id` | Unique idempotency key for this event |\r\n| `X-Om-Timestamp` | Unix time (seconds, UTC) used when signing |\r\n| `X-Om-Signature` | `sha256=<hex>` HMAC (see below) |\r\n\r\n**Signature verification (recommended on your server):**\r\n\r\n1. Read the **raw** request body bytes (do not re-serialize JSON).\r\n2. Build `canonical = {X-Om-Timestamp} + \".\" + {rawBody}`.\r\n3. Compute `expected = \"sha256=\" + HMAC_SHA256_UTF8(webhookSecret, canonical)` (hex **lowercase**).\r\n4. Compare `expected` to `X-Om-Signature` using a constant-time comparison; reject on mismatch.\r\n\r\nThe delivery JSON schema is documented as **`ApiWebhookDeliveryEventDTO`** in Schemas (see **Webhooks** operations). Test deliveries: **`POST /api/v1/webhooks/{id}/test`**.\r\n\r\n---\r\n\r\n## Errors\r\n\r\n- **400** — validation (`message` in body).\r\n- **401** — bad or missing credentials.\r\n- **404** — resource not in the practice (`message`).\r\n\r\nSchemas below include **illustrative** example values; live responses use your data."
  contact:
    name: OnboardMe Support
    email: support@onboardme.app
  version: v1
servers:
  - url: https://anzapi.onboardme.app
    description: Australia & New Zealand — ANZ-hosted practices
  - url: https://ukapi.onboardme.app
    description: United Kingdom — UK-hosted practices
  - url: https://zaapi.onboardme.app
    description: South Africa — ZA-hosted practices
security:
  - ApiClientCredentials: []
paths:
  /api/v1/bills/ledger/tax-rates:
    get:
      tags:
        - Bills
      summary: List Xero tax rates
      description: "Returns selected practice tax rates for Xero invoice lines.\r\n\r\nUse `extTaxID` on create line items (practice tax option id). Empty when Xero is not connected.\r\n\r\n**429:** Rate limit exceeded."
      responses:
        '200':
          description: OK
          content:
            text/plain:
              schema:
                type: array
                items:
                  $ref: >-
                    #/components/schemas/Application.APIData.DataObjects.BillLedgerTaxRateDTO
            application/json:
              schema:
                type: array
                items:
                  $ref: >-
                    #/components/schemas/Application.APIData.DataObjects.BillLedgerTaxRateDTO
            text/json:
              schema:
                type: array
                items:
                  $ref: >-
                    #/components/schemas/Application.APIData.DataObjects.BillLedgerTaxRateDTO
        '401':
          description: Unauthorized
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails'
            application/json:
              schema:
                $ref: '#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails'
            text/json:
              schema:
                $ref: '#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails'
        '429':
          description: Too Many Requests
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails'
            application/json:
              schema:
                $ref: '#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails'
            text/json:
              schema:
                $ref: '#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails'
        '500':
          description: Internal Server Error
components:
  schemas:
    Application.APIData.DataObjects.BillLedgerTaxRateDTO:
      type: object
      properties:
        extTaxID:
          type: integer
          format: int32
        taxName:
          type: string
          nullable: true
        taxPercent:
          type: number
          format: double
        taxType:
          type: string
          nullable: true
      additionalProperties: false
    Microsoft.AspNetCore.Mvc.ProblemDetails:
      type: object
      properties:
        type:
          type: string
          nullable: true
        title:
          type: string
          nullable: true
        status:
          type: integer
          format: int32
          nullable: true
        detail:
          type: string
          nullable: true
        instance:
          type: string
          nullable: true
      additionalProperties: {}
  securitySchemes:
    ApiClientCredentials:
      type: http
      description: >-
        Swagger / Postman only: **Client ID** as username, **Client secret** as
        password. In Postman, set this once on the **collection** (Authorization
        → Basic Auth) so all requests inherit. For production server-to-server
        code, prefer **`X-OM-Auth-ID`** and **`X-OM-Auth-Key`** headers.
      scheme: basic

````