# Light API documentation > REST API for accounting documents, invoices, payments, cards and the ledger. Base URL `https://api.light.inc`. 208 endpoints across 31 resources. Append `.md` to any page URL below for its markdown source. The whole reference in one file: https://light.inc/docs/llms-full.txt · The OpenAPI spec: https://light.inc/docs/openapi-public.json For questions about using Light itself rather than its API, see https://light.inc/help and https://light.inc/llms.txt. ## Getting started - [Introduction](https://light.inc/docs): Welcome to the Light API documentation. - [Authentication](https://light.inc/docs/getting-started/authentication): Authenticate with an API key or the OAuth 2.0 authorization code flow. - [Rate limits](https://light.inc/docs/getting-started/rate-limits): 300 requests a minute per user, 100,000 a day per organisation, and how to handle a 429. - [Pagination, filtering and errors](https://light.inc/docs/getting-started/pagination-filtering-errors): How list endpoints page and sort, the filter grammar and its limits, what an error looks like, how to retry safely, and the conventions partial updates, custom properties and file endpoints follow. - [Using Light from an AI client](https://light.inc/docs/getting-started/ai-clients): Light's MCP server for Claude and other AI clients: how it differs from the REST API, and what an agent reading this reference should know. ## Concepts - [How Light records money](https://light.inc/docs/concepts/how-light-records-money): Documents, posting and the ledger: what the API's objects are, and why every transaction has two sides. - [Reading amounts](https://light.inc/docs/concepts/reading-amounts): Minor units, debit and credit, signed and unsigned figures, and the three currencies on every ledger line. - [Document lifecycle](https://light.inc/docs/concepts/document-lifecycle): The statuses every document moves through, which endpoints move them, and what can still change after posting. - [Periods and locks](https://light.inc/docs/concepts/periods-and-locks): Why a posting is rejected with ACCOUNTING_PERIOD_CLOSED or ACCOUNTING_PERIOD_LOCKED, and what to do about it. - [Choosing an endpoint](https://light.inc/docs/concepts/choosing-an-endpoint): Common jobs mapped to the calls that do them, in order, with the rule that trips people up on each. ## Examples - [OAuth callback](https://light.inc/docs/examples/oauth-callback): Handle the OAuth 2.0 redirect and exchange the authorization code for an access token. - [Create an invoice payable](https://light.inc/docs/examples/create-invoice-payable): End-to-end: create a vendor invoice with line items and a document, then submit it for approval. - [Upload a receipt](https://light.inc/docs/examples/upload-receipt): Upload receipts for expenses and card transactions using presigned URLs. ## API reference ### Authorization The OAuth 2.0 authorization flow: start it, and exchange the code for an access token. - [Create access token](https://light.inc/docs/api-reference/authorization/create-access-token): `POST /oauth/token` — Exchanges an authorization code or refresh token for an access token - NOTE: Send the body as application/x-www-form-urlencoded , not JSON. Light forwards the six fields to its identity provider unchanged and returns its answer, so a rejected grant comes back with the provider's own status and body ( {"error": "invalid_grant", "error_description": "..."} ), not the {name, type, errors[]} envelope used elsewhere. Response keys are snake_case ( access_token , expires_in in seconds, token_type , refresh_token ), and refresh_token is null when the grant didn't produce one. - [Start authorization flow](https://light.inc/docs/api-reference/authorization/start-authorization-flow): `GET /oauth/authorize` — Redirects the user to the authorization page to start the OAuth V2 authorization flow - NOTE: This endpoint does not return JSON. It answers 303 See Other with an empty body and a Location header pointing at Light's identity provider, so send the user's browser here rather than calling it from a server. client_id and redirect_uri are required; state is optional and echoed back unchanged on the callback. The scope is fixed by Light ( openid profile email offline_access ) and the user is always shown the login and consent screens; PKCE ( code_challenge ) is not supported and any scope you pass is ignored. ### Accounting Documents List and query every accounting document, across all document types. - [List accounting documents](https://light.inc/docs/api-reference/v1--accounting-documents/list-accounting-documents): `GET /v1/accounting-documents/accounting-documents` — Returns a paginated list of accounting documents - NOTE: Every posting-capable record in one list. documentType : AP vendor bill, AR customer invoice, BP bank payment, CC customer credit, CN vendor credit note, CT card transaction, DE accounting-release (amortisation or depreciation) entry, FX revaluation entry, JE journal entry, YC year-closing entry. status : DRAFT , APPROVAL_PENDING , APPROVED , POSTED , PARTIALLY_CLEARED and CLEARED (matched against payments or credits), ARCHIVED (a draft archived, or a posted document reversed). Drafts and archived documents are included unless filtered; default order is newest first. Read totalTransactionAmount as the document's absolute total and ignore its dcSign : the sign is derived from a type-specific sum, so a journal entry's total shows C and a customer invoice's shows D . postingDate , valuationDate and ledgerName can be filtered and sorted on but are not in the response; for those and for lines, use GET /v1/ledger-transaction-lines filtered by accDocId . The document types and statuses are explained in How Light records money (/docs/concepts/how-light-records-money) and Document lifecycle (/docs/concepts/document-lifecycle). ### Accounting Release Templates Create and manage the templates that generate accounting releases, and archive the ones no longer in use. - [Archive accounting release template](https://light.inc/docs/api-reference/v1--accounting-release-templates/archive-accounting-release-template): `POST /v1/accounting-release-templates/{templateId}/archive` — Archives the given accounting release template so it can no longer be assigned to new documents. Releases already generated from it are unaffected - NOTE: Idempotent and unconditional: archiving an archived template succeeds again. The only failure is 404 ACCOUNTING_RELEASE_TEMPLATE_NOT_FOUND . - [List accounting release templates](https://light.inc/docs/api-reference/v1--accounting-release-templates/list-accounting-release-templates): `GET /v1/accounting-release-templates` — Returns a list of accounting release templates - NOTE: Archived templates are included; add filter=status:eq:ACTIVE to exclude them. Default order is newest first. - [Create accounting release template](https://light.inc/docs/api-reference/v1--accounting-release-templates/create-accounting-release-template): `POST /v1/accounting-release-templates` — Creates a new accounting release template. The ledger accounts and type chosen here are fixed for the lifetime of the template - NOTE: residualAmount and reducingRate are not mutually exclusive as the description says: at least one is required for REDUCING_BALANCE ( ACCOUNTING_RELEASE_TEMPLATE_REDUCING_BALANCE_MISSING_RATE_OR_RESIDUAL ), and both may be sent and stored. REDUCING_BALANCE is allowed only for type FIXED_ASSET , AP or JE ; type: CONTRACT requires contractAccountId . reducingRate must be in [0, 1) , residualAmount above zero, initialAmountPercentage + residualAmountPercentage at most 1. None of the account ids may point at a payables or receivables control account ( ACCOUNTING_RELEASE_TEMPLATE_RECONCILABLE_ACCOUNT_NOT_ALLOWED ). - [Get accounting release template](https://light.inc/docs/api-reference/v1--accounting-release-templates/get-accounting-release-template): `GET /v1/accounting-release-templates/{templateId}` — Returns an accounting release template by ID - NOTE: An unknown id, or one from another company, is 404 . Same permission as listing templates. - [Update accounting release template](https://light.inc/docs/api-reference/v1--accounting-release-templates/update-accounting-release-template): `PATCH /v1/accounting-release-templates/{templateId}` — Updates an existing accounting release template. Only the fields present in the request body are changed. The ledger accounts and type are fixed at creation and cannot be updated. To use different ones, create a new template and archive this one. - NOTE: No status check: an ARCHIVED template can still be updated and stays archived. name , method and accumulatePastReleasesEnabled cannot be cleared; defaultDuration , the percentages, residualAmount , reducingRate and context follow the null -clears rule. The merged result is re-validated with the create rules. ### Attachments Upload, list and manage the documents attached to a record. - [List attachments](https://light.inc/docs/api-reference/v1--attachments/list-attachments): `GET /v1/attachments` — Returns a list of attachments associated with a specific resource - NOTE: Returns a bare JSON array, not the paginated {records, hasMore} envelope, and takes no filter , sort or limit . resourceId is required; an unknown id yields [] rather than 404 . - [Create attachment](https://light.inc/docs/api-reference/v1--attachments/create-attachment): `POST /v1/attachments` — Creates a new attachment record - NOTE: Step three of the upload flow: documentKey is the key returned by POST /v1/attachments/upload-url , and the bytes must already be there — otherwise ATTACHMENT_FILE_NOT_FOUND . A resource can carry at most 10 attachments ( ATTACHMENT_NUMBER_FOR_RESOURCE_EXCEEDED ); the same documentKey or fileName twice on one resource fails with ATTACHMENT_DOCUMENT_KEY_ALREADY_EXISTS / ATTACHMENT_FILE_NAME_FOR_RESOURCE_ALREADY_EXISTS . resourceId is not validated. metadata is only read when Light emails a sales invoice or customer credit ( type: "AR" , shouldAttachToEmail ); on other resources it has no effect. - [Generate attachment upload URL](https://light.inc/docs/api-reference/v1--attachments/generate-attachment-upload-url): `POST /v1/attachments/upload-url` — Generates a secure upload URL for attachment files - NOTE: Step one of three; see Files (/docs/getting-started/pagination-filtering-errors files). The URL is valid for five minutes . When you PUT the bytes to it, set Content-Type to the contentType you asked for and send every entry of the returned metadata as a request header — they are signed into the URL and the upload is rejected without them. Then register the file with POST /v1/attachments , passing the returned key as documentKey . fileName must be unique per resourceId ; reusing one fails with ATTACHMENT_FILE_NAME_FOR_RESOURCE_ALREADY_EXISTS . contentType must be one of the PDF, image ( image/ ), text or Office document types; anything else fails with UNSUPPORTED_FILE_MIME_TYPE . The key is generated by Light; you cannot choose it. There is no resourceType in this API: resourceId is the id of the record (an invoice, a purchase order, ...) and it is not checked against anything, so a typo produces an attachment nothing will ever show. - [Delete attachment](https://light.inc/docs/api-reference/v1--attachments/delete-attachment): `DELETE /v1/attachments/{attachmentId}` — Deletes a specific attachment and its associated file - NOTE: The stored file is not deleted. Despite the description, this removes only the attachment record; the uploaded object stays in storage. Deleting is refused with ATTACHMENT_CANNOT_BE_DELETED_FOR_POSTED_DOCUMENT when the resource is an accounting document that has been posted. Any principal in the company may delete, not only the uploader. Success is 204 with no body. - [Get attachment document](https://light.inc/docs/api-reference/v1--attachments/get-attachment-document): `GET /v1/attachments/{attachmentId}/document` — Returns the document file for a specific attachment - NOTE: Returns a 307 Temporary Redirect with an empty body, not the file. The Location is a pre-signed download URL valid for two hours that serves the file with its stored content type and a Content-Disposition: attachment header. Follow it without your Light Authorization header. Unknown id: 404 ATTACHMENT_NOT_FOUND . - [Get attachment options](https://light.inc/docs/api-reference/v1--attachments/get-attachment-options): `OPTIONS /v1/attachments/{attachmentId}/document` — Handles CORS preflight requests for attachment document access - NOTE: CORS preflight for browsers that download the file directly. Answers 200 with the allowed-origin headers and no body, without authentication. API clients never need to call it. ### Bank Accounts Create and access bank accounts. Creating one also creates its linked ledger account atomically. - [Get bank accounts of the company](https://light.inc/docs/api-reference/v1--bank-accounts/get-bank-accounts-of-the-company): `GET /v1/bank-accounts` — Returns all bank accounts of the company - NOTE: Not paginated and not filterable: every bank account of the company, with no status to tell active from retired ones. - [Create bank account](https://light.inc/docs/api-reference/v1--bank-accounts/create-bank-account): `POST /v1/bank-accounts` — Creates a bank account along with its linked ledger (chart-of-accounts) entry in a single transaction. The ledger account code must be a unique 6-digit integer within the company's chart of accounts. - NOTE: The ledger account is created with fixed attributes you cannot choose: type BANK , active, revalued for FX at end-of-month rates, in the bank account's currency, scoped to the one companyEntityId . Its label defaults to the bank account's name , not the bank name as the description says. code must be exactly six digits ( LEDGER_ACCOUNT_CODE_LENGTH ) and unused ( BANK_ACCOUNT_LEDGER_ACCOUNT_CODE_ALREADY_EXISTS ; nothing is created). bankProvider: OTHER requires bankName ; other providers fill it in. The response's type and paymentSchedulingEligibility are derived from the provider (only Airwallex accounts are VIRTUAL ; only providers Light can pay through are INTERNAL , everything else INELIGIBLE ). Company-admin role only. - [List bank transactions](https://light.inc/docs/api-reference/v1--bank-accounts/list-bank-transactions): `GET /v1/bank-accounts/{bankAccountId}/bank-transactions` — Returns a paginated list of bank transactions for the specified bank account, including the balance - NOTE: balance is not the page's balance: it is D minus C over every transaction matching the filter, and, unlike GET .../balance , it includes EXCLUDED transactions unless you filter them out. Cursor pagination doesn't apply here: cursor is treated as an offset and the response has no nextCursor or hasMore , so page with offset , limit and total . Default order is newest-created first, not by date . - [Create bank transactions](https://light.inc/docs/api-reference/v1--bank-accounts/create-bank-transactions): `POST /v1/bank-accounts/{bankAccountId}/bank-transactions` — Creates bank transactions in batch. Maximum 500 transactions per request. Duplicate transactions (same transactionId for the same bank account) are silently skipped. - NOTE: dcSign is the reverse of the description. A bank account is an asset, so D is money in (increases the balance) and C is money out ; this is what the bank feeds, the CSV import and both balance endpoints use. Sending D for outflows inverts every balance. Duplicate handling is silent: a row whose transactionId already exists for this API on the account is skipped and simply missing from the response (a full retry returns [] ). Rows with transactionId: null are never de-duplicated, and a transaction already present from a bank feed or CSV import under the same id is not treated as a duplicate either. Over 500 rows is BANK_TRANSACTION_BATCH_SIZE_EXCEEDED , an empty list BANK_TRANSACTION_BATCH_EMPTY . Nothing is posted to the ledger: transactions land as UNMATCHED for bank reconciliation only. A negative amount is accepted and flips the arithmetic, so keep amounts positive and use dcSign . - [List bank account activity events](https://light.inc/docs/api-reference/v1--bank-accounts/list-bank-account-activity-events): `GET /v1/bank-accounts/{bankAccountId}/activity-events` — The bank account's activity timeline, newest first: who did what to it, when, and which fields changed. Requires permission to view this bank account. The entityId and entityType filters are fixed by the path here; passing them can only narrow the result. - [Get bank account balance](https://light.inc/docs/api-reference/v1--bank-accounts/get-bank-account-balance): `GET /v1/bank-accounts/{bankAccountId}/balance` — Returns the bank statement balance and ledger balance for a bank account as of the given date. If asOf is omitted, today's balance is returned. The bank balance is derived from the opening balance plus all bank transactions on or before asOf ; the ledger balance is the sum of ledger transaction lines posted on or before asOf for the linked ledger account. - NOTE: bankBalance is the stored closing balance plus the signed non-EXCLUDED transactions dated after its day up to asOf ; it is null when no balance has been set or when asOf is before the day the balance was set for . ledgerBalance is the debit-positive sum, in the transaction currency, of lines on the linked ledger account posted up to asOf , excluding FX revaluations, archived documents and reversed lines; it is 0 rather than null when there are none. - [Upsert bank account balance](https://light.inc/docs/api-reference/v1--bank-accounts/upsert-bank-account-balance): `PUT /v1/bank-accounts/{bankAccountId}/balance` — Creates or updates the opening balance for a bank account. Only one balance per bank account is allowed — subsequent calls update the existing balance. - NOTE: This stores a closing (end-of-day) balance, not an opening one. balanceAt is normalised to 23:59:59 UTC of its day, and transactions dated on or before that day are treated as already included in the balance; only later transactions are added on top. So give the statement balance at the end of that day, and load transactions dated after it. A second call replaces both balance and balanceAt for the account. - [Get bank transaction](https://light.inc/docs/api-reference/v1--bank-accounts/get-bank-transaction): `GET /v1/bank-accounts/{bankAccountId}/bank-transactions/{bankTransactionId}` — Returns a specific bank transaction by ID - NOTE: 404 unless the transaction belongs to that bank account. ### Invoice Payables Create, approve, decline and mark as paid — bills and their line items, end to end. - [List invoice payables](https://light.inc/docs/api-reference/v1--invoice-payables/list-invoice-payables): `GET /v1/bff/invoice-payables` — Returns a paginated list of invoice payables - NOTE: This is the only list operation for invoice payables — /v1/invoice-payables has no GET . The single-record read is GET /v1/invoice-payables/{invoicePayableId} , and it returns a different shape : list rows have no lineItems , customProperties , outstandingBalance , purchaseOrderId , FX rates or payment references, and instead carry vendor , user , nextApprover , companyEntityName and documentKey . nextApprover is null when the next approver is a user group. include accepts only REIMBURSEMENT . The documentNumber filter matches the invoice number. A credential without the company-admin, AP-preparation or auditor role must filter on its own user id ( approverUserId:eq: