# Edara API v3: Journal Entries

Read the [guide](../guide.md) first: authentication, paging, errors, rate limits and the shared `PagedResult`, `BatchResult` and `*Reference` shapes.

- `GET /v3/journal-entries`: Lists journal entries.
- `POST /v3/journal-entries`: Creates a journal entry.
- `PUT /v3/journal-entries/code/{code}`: Updates a journal entry by code.
- `DELETE /v3/journal-entries/code/{code}`: Deletes a journal entry by code.
- `POST /v3/journal-entries/payment`: Creates a payment journal entry.

## Endpoints

### `GET /v3/journal-entries`: Lists journal entries.

Operation `GetJournalEntries` · permission `read:journal-entry`

**Query string**: `GetJournalEntriesQuery`

| field | type | default | validation | description |
|---|---|---|---|---|
| `offset` | `int?` | 0 | GreaterThanOrEqualTo(0) .When(x => x.Offset.HasValue) | Number of items to skip before returning results. Defaults to 0. |
| `limit` | `int?` | 100 | InclusiveBetween(1, PaginatedQuery.MaxLimit) .When(x => x.Limit.HasValue) | Maximum number of items to return. Defaults to 100, maximum 1000. |
| `code` | `string` |  |  | The journal entry code. |
| `paperNumber` | `string` |  |  | The journal entry paper number. |
| `relatedIOCode` | `string` |  |  | The related issue-offering code. |
| `accountId` | `int?` |  | GreaterThan(0) .When(x => x.AccountId.HasValue) | Returns journal entries where any detail line hits this general-ledger account. |
| `customerId` | `int?` |  | GreaterThan(0) .When(x => x.CustomerId.HasValue) | Narrows to journal entries whose matched account line carries this customer. |
| `supplierId` | `int?` |  | GreaterThan(0) .When(x => x.SupplierId.HasValue) | Narrows to journal entries whose matched account line carries this supplier. |
| `costCenterId` | `int?` |  | GreaterThan(0) .When(x => x.CostCenterId.HasValue) | Narrows to journal entries whose matched account line is tagged with this cost center. |
| `dateFrom` | `DateTime?` |  |  | Only journal entries on or after this document date. |
| `dateTo` | `DateTime?` |  |  | Only journal entries on or before this document date. |
| `isPostedOnly` | `bool?` | true |  | When false, unposted/draft journal entries are also included. Defaults to true. |
| `currencyId` | `int?` |  | GreaterThan(0) .When(x => x.CurrencyId.HasValue) | Only journal entries recorded in this currency. |
| _(object rule)_ | | | RuleFor(x => x) .Must(HaveOrderedDateRange) .WithMessage("dateFrom must be on or before dateTo.") | |

**Responses**: OK `PagedResult<JournalEntryResponse>`: The matching journal entries.

**Notes**

- `isPostedOnly` defaults to `true`. POST /v3/journal-entries always saves unposted, so an entry you just created is not listed unless you send `isPostedOnly=false`.
- The list returns every accounting document type, not only journal entries. For example, `SI`, `CI`, `CO` and `NR` documents appear too, so check `documentType` in each item.
- Documents marked as deleted are never returned.
- Recurring journal entry templates are included here, although account balance calls leave them out.
- `code` is an exact match on either the document code or the journal entry code. `paperNumber` and `currencyId` are exact matches too.
- `dateFrom` and `dateTo` ignore any time part, and `dateTo` includes the whole day.
- `accountId`, `customerId`, `supplierId` and `costCenterId` must all match on the same line of an entry. Each returned entry still includes all of its lines.
- `relatedIOCode` returns the documents that are related to the IO document with that code.
- Results are ordered by document date, newest first, then by id descending. `totalCount` is the total number of matches.
- When nothing matches you get `200` with an empty list.
- `limit` must be between 1 and 1000 and `offset` must not be negative. Values outside that range return `400` instead of being adjusted.
- Sending both `customerId` and `supplierId` returns `400`, and so does a `dateFrom` later than `dateTo`.

---

### `POST /v3/journal-entries`: Creates a journal entry.

Operation `CreateJournalEntry` · permission `create:journal-entry`

**Body**: `CreateJournalEntryRequest`

| field | type | default | validation | description |
|---|---|---|---|---|
| `isInvoicingJE` | `bool?` |  |  | Whether this journal entry was generated by Invoicing 3.0. Optional; defaults to false. |
| `documentDate` | `DateTime?` |  | NotNull() | The accounting document date. |
| `paperNumber` | `string` |  |  | The external paper number. |
| `responsibleId` | `int?` |  | GreaterThan(0).When(x => x.ResponsibleId.HasValue) | The responsible user identifier. |
| `notes` | `string` |  |  | Document notes. |
| `currencyId` | `int?` |  | GreaterThan(0).When(x => x.CurrencyId.HasValue) | The optional currency identifier. |
| `exchangeRate` | `decimal?` |  | GreaterThan(0m).When(x => x.ExchangeRate.HasValue) | The exchange rate when a currency is supplied. |
| `relatedDocumentCode` | `string` |  |  | The code of a related document. |
| `linkedDocumentCode` | `string` |  |  | The code of an additional linked document. |
| `accountingDocumentDetails` | `List<CreateJournalEntryDetailRequest>` |  | NotNull().NotEmpty(); each: SetValidator(new CreateJournalEntryDetailRequestValidator()) | The detail lines. |

**Responses**: Created `JournalEntryMutationResponse`: Journal entry created.

**Business errors** (HTTP 409, match on `errorCode`):

- `CannotInsertDocumentBeforeClosingDate`: The document date is before the closing date.
- `CK_NoCustomerOrSupplierSelectedforAccount`: Occurs when posting a journal line to a control account that requires a linked customer or supplier, but neither party was supplied.
- `CK_NocustomerSelectedforAR`: Occurs when posting a journal line to an AR account that requires a linked customer, but no customer was selected.
- `CK_NoSupplierSelectedforAP`: Occurs when posting a journal line to an AP account that requires a linked supplier, but no supplier was selected.
- `ExchangeRateDecimalsExceedLimit`: The exchange rate has too many decimal places.
- `PreventSaveDocumentsInFutureDate`: The document date is in the future.

**Notes**

- `documentDate` is required and `accountingDocumentDetails` must contain at least one line. Each line needs `accountId`, an `amount` above 0 and `amountStatus`.
- `currencyId`, `exchangeRate` and `responsibleId` must be above 0 when you send them.
- The entry is always saved unposted, whatever the organization's posting setting. It is left out of GET /v3/journal-entries and of account balances unless you send `isPostedOnly=false`.
- The entry needs at least one debit line and one credit line, or you get `400`.
- An accounts receivable (AR) line needs a customer or a supplier, an accounts payable (AP) line needs a supplier, and a line cannot carry both. A customer or supplier must be linked to the line's account. Breaking any of these rules returns `400`.
- A `costCenterId` on an account that does not accept cost centers returns `400`. Cash in and cash out ignore it instead.
- If you omit the cost center, the account's default cost center is used. If the account has no default and the organization requires cost centers, you get `400`.
- If you omit the customer or supplier, an AP line gets the account's single linked supplier, and an AR line with no linked suppliers gets its single linked customer.
- Whether an unbalanced entry (total debit not equal to total credit) is rejected depends on an organization setting. Balance the entry yourself and do not rely on the API to catch it.
- Without `currencyId`, the rate is stored as 1 and any `exchangeRate` you send is ignored.
- With `currencyId` and no `exchangeRate`, the currency's current rate at the time of the call is used, not the rate on `documentDate`.
- An `exchangeRate` with more than 6 decimal places returns `409`.
- A future `documentDate` is accepted unless the organization has turned that off, so code defensively. A date before the closing date returns `409`.
- `linkedDocumentCode` can hold several document codes separated by commas. Each one is linked to the entry.
- The response contains only `documentCode`.

---

### `PUT /v3/journal-entries/code/{code}`: Updates a journal entry by code.

Operation `UpdateJournalEntryByCode` · permission `update:journal-entry`

> - Returns 204 with no body on success.

**Parameters**

| name | in | type | default | description |
|---|---|---|---|---|
| `code` | route | `string` |  | The journal entry code. |

**Body**: `UpdateJournalEntryRequest`

| field | type | default | validation | description |
|---|---|---|---|---|
| `documentDate` | `DateTime?` |  | NotNull() | The accounting document date. |
| `paperNumber` | `string` |  |  | The external paper number. |
| `responsibleId` | `int?` |  | GreaterThan(0).When(x => x.ResponsibleId.HasValue) | The responsible user identifier. |
| `notes` | `string` |  |  | Document notes. |
| `currencyId` | `int?` |  | GreaterThan(0).When(x => x.CurrencyId.HasValue) | The optional currency identifier. |
| `exchangeRate` | `decimal?` |  | GreaterThan(0m).When(x => x.ExchangeRate.HasValue) | The exchange rate when a currency is supplied. |
| `accountingDocumentDetails` | `List<UpdateJournalEntryDetailRequest>` |  | NotNull().NotEmpty(); each: SetValidator(new UpdateJournalEntryDetailRequestValidator()) | The detail lines. |

**Responses**: NoContent: Journal entry updated.

**Business errors** (HTTP 409, match on `errorCode`):

- `CannotUpdateDeletedRelatedToCashedCNDN`: The entry is tied to a deleted or cashed document.
- `CannotUpdateDocumentBeforeClosingDate`: The document date is before the closing date.
- `DirtyRead`: The document changed while it was being updated.
- `DocumentCannotUpdateAfterPosting`: The journal entry has already been posted.
- `ExchangeRateDecimalsExceedLimit`: The exchange rate has too many decimal places.
- `ItemCannotDeleteItInUse`: The journal entry is referenced by another document or process.
- `PhantomRead`: The related document details changed while it was being updated.
- `PostedDocumentsCannotBeUpdatedOrDeleted`: The journal entry is posted and cannot be changed.

**Notes**

- A `code` that starts with `JE` is looked up as a journal entry code. Any other value is looked up as a document code.
- An unknown `code` returns `404`, and so does a document with no lines. A document that is already posted returns `409` with `PostedDocumentsCannotBeUpdatedOrDeleted`.
- The lines you send must cover every existing line id. Lines whose `rowState` is not `Added` must match the stored line ids exactly, or you get `409` with `DirtyRead`.
- `rowState` defaults to `Modified`. `Deleted` removes the line, and `Added` inserts a new line and ignores any id you send.
- Header fields are overwritten, and a field you leave out or send as null is cleared.
- Omitting `currencyId` clears the currency and sets the rate to 1. Sending `currencyId` without `exchangeRate` keeps the stored rate.
- The same rules as create apply, checked against the lines that are not marked `Deleted`.
- An update can post the document. If the organization posts in real time and every line has an account, a successful update posts it, even though create left it unposted. Code defensively.
- A date before the closing date returns `409` with `CannotUpdateDocumentBeforeClosingDate`. An `exchangeRate` with more than 6 decimal places returns `409`.
- A successful update returns `204`.

---

### `DELETE /v3/journal-entries/code/{code}`: Deletes a journal entry by code.

Operation `DeleteJournalEntryByCode` · permission `delete:journal-entry`

**Parameters**

| name | in | type | default | description |
|---|---|---|---|---|
| `code` | route | `string` |  | The journal entry code. |

**Responses**: NoContent: (not declared; read from the method body)

**Business errors** (HTTP 409, match on `errorCode`):

- `CannotDeleteSystemJournalEntry`: The journal entry is a system-generated document (e.g. created for an issue offering) and cannot be deleted.
- `CannotUpdateDeletedRelatedToCashedCNDN`: The entry is tied to a deleted or cashed document.
- `DirtyRead`: The document changed while it was being deleted.
- `ItemCannotDeleteItInUse`: The journal entry is referenced by another document or process.
- `PostedDocumentsCannotBeUpdatedOrDeleted`: The journal entry is posted and cannot be deleted.

**Notes**

- `code` is looked up the same way as on update: a value that starts with `JE` is a journal entry code, and any other value is a document code.
- An unknown `code` returns `404`. A system document returns `409` with `CannotDeleteSystemJournalEntry`, and a posted document returns `409` with `PostedDocumentsCannotBeUpdatedOrDeleted`.
- The delete is permanent. The document, its lines, and its relations and links to other documents are removed.
- A document that other records still reference returns `409` with `ItemCannotDeleteItInUse`. Other delete failures can return the same `errorCode`, so do not treat it as proof that the document is in use.
- `DirtyRead` is declared for this endpoint but it is never returned.
- A successful delete returns `204`.

---

### `POST /v3/journal-entries/payment`: Creates a payment journal entry.

Operation `CreatePaymentJournalEntry` · permission `create:journal-entry`

**Body**: `CreatePaymentJournalEntryRequest`

| field | type | default | validation | description |
|---|---|---|---|---|
| `documentDate` | `DateTime?` |  | NotNull() | The accounting document date. |
| `paperNumber` | `string` |  |  | The external paper number. |
| `responsibleId` | `int?` |  | GreaterThan(0).When(x => x.ResponsibleId.HasValue) | The responsible user identifier. |
| `notes` | `string` |  |  | Document notes. |
| `currencyId` | `int?` |  | GreaterThan(0).When(x => x.CurrencyId.HasValue) | The optional currency identifier. |
| `exchangeRate` | `decimal?` |  | GreaterThan(0m).When(x => x.ExchangeRate.HasValue) | The exchange rate when a currency is supplied. |
| `salesOrderToPay` | `string` |  | NotEmpty() | The code of the sales order being paid. Required. |
| `linkedDocumentCode` | `string` |  |  | The code of an additional linked document. |
| `accountingDocumentDetails` | `List<PaymentJournalEntryDetailRequest>` |  | NotNull().NotEmpty(); each: SetValidator(new PaymentJournalEntryDetailRequestValidator()) | The detail lines. |

**Responses**: Created `JournalEntryResponse`: Payment journal entry created.

**Business errors** (HTTP 409, match on `errorCode`):

- `CannotInsertDocumentBeforeClosingDate`: The document date is before the closing date.
- `CK_NoCustomerOrSupplierSelectedforAccount`: Occurs when posting a journal line to a control account that requires a linked customer or supplier, but neither party was supplied.
- `CK_NocustomerSelectedforAR`: Occurs when posting a journal line to an AR account that requires a linked customer, but no customer was selected.
- `CK_NoSupplierSelectedforAP`: Occurs when posting a journal line to an AP account that requires a linked supplier, but no supplier was selected.
- `ExchangeRateDecimalsExceedLimit`: The exchange rate has too many decimal places.
- `PreventSaveDocumentsInFutureDate`: The document date is in the future.
- `RelatedSalesOrderAlreadyCashedIn`: Occurs when the linked sales order has already been settled by cash-in, so another payment journal entry cannot be created from it.
- `RelatedSalesOrderCancelled`: Occurs when the linked sales order is already canceled, so the payment journal entry cannot be created from it.

**Notes**

- `documentDate` and `salesOrderToPay` are required, and `accountingDocumentDetails` must contain at least one line, even though the parameter table does not mark them. Each line follows the same rules as POST /v3/journal-entries.
- If `salesOrderToPay` does not match an existing document, the request returns `400`, not `404`. If the order is cancelled, it returns `409` `RelatedSalesOrderCancelled`.
- Every credit line is assigned the customer of the sales order. Payment lines have no party fields, so a debit line on an accounts receivable or accounts payable account returns `400`.
- Creating the payment entry does not mark the sales order as cashed in. It only links the entry to the order.
- The entry is always saved unposted. All other rules and the currency handling match POST /v3/journal-entries.

## Types

### `AccountReference`

Lightweight account reference (id + code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The account id. |
| `code` | `string` | The account code. |
| `name` | `string` | The account name. |

### `CostCenterReference`

Lightweight cost-center reference (id + code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The cost-center id. |
| `code` | `string` | The cost-center code. |
| `name` | `string` | The cost-center name. |

### `CreateJournalEntryDetailRequest`

Request describing a new journal entry detail line.

| field | type | validation | description |
|---|---|---|---|
| `accountId` | `int?` | GreaterThan(0) | The account identifier. |
| `costCenterId` | `int?` | GreaterThan(0) .When(x => x.CostCenterId.HasValue) | The optional cost center identifier. |
| `amount` | `decimal` | GreaterThan(0m) | The line amount. |
| `amountStatus` | `DebitCreditSide?` | NotNull(); Must(value => !value.HasValue \|\| Enum.IsDefined(typeof(DebitCreditSide), value.Value)) | The amount status. |
| `comments` | `string` |  | Optional line comments. |
| `customerId` | `int?` | GreaterThan(0) .When(x => x.CustomerId.HasValue) | The optional customer identifier. |
| `supplierId` | `int?` | GreaterThan(0) .When(x => x.SupplierId.HasValue) | The optional supplier identifier. |

### `CurrencyReference`

Lightweight currency reference (id + ISO code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The currency id. |
| `code` | `string` | The ISO 4217 currency code. |
| `name` | `string` | The currency name. |

### `CustomerReference`

Lightweight customer reference (id + code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The customer id. |
| `code` | `string` | The customer code. |
| `name` | `string` | The customer name. |

### `DebitCreditSide`

Debit/credit side for accounting lines.

Values (sent/returned as the name): `Debit`=0, `Credit`=1

### `DocumentRowState`

Row-state for mutable document detail lines.

Values (sent/returned as the name): `Added`=0, `Modified`=1, `Deleted`=2

### `JournalEntryDetailResponse`

Response describing a journal entry detail line.

| field | type | description |
|---|---|---|
| `id` | `int` | The detail identifier. |
| `account` | `AccountReference` | The account reference. |
| `costCenter` | `CostCenterReference` | The optional cost center reference. |
| `amount` | `decimal` | The line amount. |
| `amountStatus` | `DebitCreditSide?` | The amount status. |
| `comments` | `string` | Optional line comments. |
| `customer` | `CustomerReference` | The optional customer reference. |
| `supplier` | `SupplierReference` | The optional supplier reference. |

### `JournalEntryMutationResponse`

Response describing a created journal entry.

| field | type | description |
|---|---|---|
| `documentCode` | `string` | The generated document code. |

### `JournalEntryResponse`

Response describing a journal entry.

| field | type | description |
|---|---|---|
| `id` | `int` | The document identifier. |
| `documentCode` | `string` | The document code. |
| `jeCode` | `string` | The journal entry's own general-ledger code. |
| `documentType` | `string` | The source document type code (for example SI, CI, or JE). |
| `isPosted` | `bool` | Whether the journal entry is posted. |
| `documentDate` | `DateTime` | The accounting document date. |
| `paperNumber` | `string` | The external paper number. |
| `responsibleId` | `int?` | The responsible user identifier. |
| `notes` | `string` | Document notes. |
| `currency` | `CurrencyReference` | The optional currency reference. |
| `exchangeRate` | `decimal` | The exchange rate. |
| `isSystemDocument` | `bool` | Whether the journal entry is system-generated. |
| `isInvoicingJE` | `bool` | Whether this journal entry was generated by Invoicing 3.0. |
| `relatedOrder` | `string` | The related sales or purchase order code. |
| `relatedWarehouseDoc` | `string` | The related warehouse issue or receipt document code. |
| `accountingDocumentDetails` | `List<JournalEntryDetailResponse>` | The detail lines. |

### `PaymentJournalEntryDetailRequest`

Request describing a payment journal entry detail line.

| field | type | validation | description |
|---|---|---|---|
| `accountId` | `int?` | GreaterThan(0) | The account identifier. |
| `costCenterId` | `int?` | GreaterThan(0) .When(x => x.CostCenterId.HasValue) | The optional cost center identifier. |
| `amount` | `decimal` | GreaterThan(0m) | The line amount. |
| `amountStatus` | `DebitCreditSide?` | NotNull(); Must(value => !value.HasValue \|\| Enum.IsDefined(typeof(DebitCreditSide), value.Value)) | The amount status. |
| `comments` | `string` |  | Optional line comments. |

### `SupplierReference`

Lightweight supplier reference (id + code + name).

| field | type | description |
|---|---|---|
| `id` | `int` | The supplier id. |
| `code` | `string` | The supplier code. |
| `name` | `string` | The supplier name. |

### `UpdateJournalEntryDetailRequest`

Request describing a journal entry detail line on update.

| field | type | validation | description |
|---|---|---|---|
| `id` | `int` |  | The detail identifier. |
| `accountId` | `int?` | GreaterThan(0) | The account identifier. |
| `costCenterId` | `int?` | GreaterThan(0) .When(x => x.CostCenterId.HasValue) | The optional cost center identifier. |
| `amount` | `decimal` | GreaterThan(0m) | The line amount. |
| `amountStatus` | `DebitCreditSide?` | NotNull(); Must(value => !value.HasValue \|\| Enum.IsDefined(typeof(DebitCreditSide), value.Value)) | The amount status. |
| `comments` | `string` |  | Optional line comments. |
| `customerId` | `int?` | GreaterThan(0) .When(x => x.CustomerId.HasValue) | The optional customer identifier. |
| `supplierId` | `int?` | GreaterThan(0) .When(x => x.SupplierId.HasValue) | The optional supplier identifier. |
| `rowState` | `DocumentRowState?` | Must(value => !value.HasValue \|\| Enum.IsDefined(typeof(DocumentRowState), value.Value)) | The row state for the update. |
