# Document Attachments _(Realtime-enabled)_

`GET` `POST` _(Table: Document Attachment)_

```
eOne/core/v1.0/documentAttachments
```

Attach a file to almost any record. It writes to Business Central's standard **Document Attachment** table, so the file shows up in the Attachments factbox on the record and flows to posted documents like any manually added attachment.

## Identify the parent record

Send a `parentType` (the kind of record) plus one of two keys:

- [integrationId](https://eonesolutions.github.io/eone-bc-integration/integration-id.md) (the eOne external key already stamped on the record)
- `parentNumber` (the Business Central number). The `integrationId` takes precedence if you send both.

> **Tip:** For open sales/purchase documents, `parentType` also carries the document type (e.g. `salesOrder` vs `salesInvoice`) so it lands on the right document.

## Receipts on staged documents

Staged [ledger documents](https://eonesolutions.github.io/eone-bc-integration/core-customerPayments.md) and [expenses](https://eonesolutions.github.io/eone-bc-integration/core-expenses.md) are attachable too, which is how an expense report arrives with the receipt that justifies it. Use `stagedLedgerDocument` for anything on the eOne Ledger Documents list, and `stagedExpense` for an expense report. The file shows up in the **Attachments** factbox on the eOne Expense Card and the eOne Ledger Document Card, so the person approving the document sees the receipt beside the amounts.

`POST` `/documentAttachments`

```
{
  "parentType": "stagedExpense",
  "integrationId": "EXPENSIFY-88214",
  "fileName": "receipt.jpg",
  "fileContent": "/9j/4AAQSkZJRgABA… (base64)"
}
```

These documents have no Business Central number of their own, so `parentNumber` is their entry no. Send the [integrationId](https://eonesolutions.github.io/eone-bc-integration/integration-id.md) instead wherever you can: it is the key you already have, and it is the one key that survives processing.

## Supply the file: base64 or a public URL

Send the content one of two ways (only use one at a time):

- Inline base64 in `fileContent`
- A public `fileUrl` the service downloads server-side.

`fileName` (with its extension) is always required, as it drives the stored file name, extension, and file type.

`POST` `/documentAttachments`

```
{
  "parentType": "salesOrder",
  "integrationId": "SHOPIFY-1001",
  "fileName": "packing-slip.pdf",
  "fileContent": "JVBERi0xLjcKJ… (base64)"
}
```

```
{
  "parentType": "item",
  "parentNumber": "1000",
  "fileName": "spec-sheet.pdf",
  "fileUrl": "https://cdn.example.com/specs/1000.pdf"
}
```

## As a child collection of the parent

`documentAttachments` is also exposed as an `$expand` child on the records it attaches to so you can pull a record together with its files in one call:

```
GET /customers(<id>)?$expand=documentAttachments
GET /salesOrders(<id>)?$expand=documentAttachments
```

The child is **read + delete** only: list a parent's attachments, or DELETE one. You cannot attach a file inline while creating the parent. POST to the top-level `documentAttachments` endpoint instead.

## Reading a file back

GET echoes the resolved `parentType` and `integrationId` and returns the stored file as base64 in `fileContent`, alongside `fileName`, `fileExtension`, and `fileType`.

> **Caution:** Because the base64 body can be large, fetch a single attachment by `id` (or use `$select` to omit `fileContent`) rather than listing the whole set with content.

## Immutable: delete and re-create to change

Attachments cannot be PATCHed. To replace one, DELETE and recreate it.

## How long a file is kept

A file lives as long as the record it hangs on.

For most records that is the whole story. Staged documents are different, because a staged document is temporary by design, and the rules are worth knowing before you attach anything to one:

- **Processed.** The staged document is consumed and replaced by a [processed document](https://eonesolutions.github.io/eone-bc-integration/core-processedDocuments.md). Its files and notes move with it. The attachment keeps its own `id`, so `GET /documentAttachments(<id>)` still returns the file afterwards, now reporting `parentType` `processedLedgerDocument` or `processedExpense` against the same `integrationId`. The move happens inside the same transaction as the posting, so a run that fails leaves the file on the staged document, ready for the retry.
- **Discarded.** Deleting a staged document deletes its files. There is no processed document to move them to and nothing else names them, so a file left behind would be unreachable rather than merely misfiled.
- **Aged out.** Processed documents are subject to Business Central's standard retention policies. When a processed document is removed, whether by a retention policy or by an administrator deleting it, its files go with it. If a receipt has to outlive the accounting record, keep it somewhere outside Business Central as well.

> **Caution:** If you attach by `integrationId`, the parent must already carry that key. Ledger-entry tables (G/L Entry, Item Ledger Entry) are not supported: Business Central's Document Attachment does not attach to them.

## Document Attachments fields

_API column → BC field._

**Legend**

- _(req)_ required on create
- _(res)_ resolver
- _(cur)_ currency-translated
- _(ro)_ read-only
- _(ext)_ added to this endpoint by another extension (BC field name shown; its API column name is not known here)

| API | BC field | Notes |
| --- | --- | --- |
| `id` | SystemId | BC record GUID. _(ro)_ |
| `bcCompanyId` | bcCompanyId | The company this row belongs to - the same GUID that appears in the request URL. Present on every endpoint so a store pooling several companies can keep them apart without relying on which URL it called. _(ro)_ |
| `bcCompanyName` | bcCompanyName | The name of the company this row belongs to. _(ro)_ |
| `parentType` | parentType | Kind of record to attach to (camelCase: customer, item, salesOrder, postedSalesInvoice, stagedExpense, …); maps to the BC Table ID and, for documents, the document type. Files on a staged document (stagedLedgerDocument / stagedExpense) move to processedLedgerDocument / processedExpense when it is processed. _(req)_ |
| `integrationId` | integrationId | External key of the record to attach to; found by its EONEIntegrationKey. One of integrationId / parentNumber; wins if both sent. |
| `fileName` | fileName | File name including extension on create; drives the stored name, extension, and file type. _(req)_ |
| `fileContent` | fileContent | The file as base64 on create; returned as base64 on read. |
| `fileUrl` | fileUrl | Public URL the file is fetched from on create. Alternative to fileContent; send one, not both. |
| `fileExtension` | File Extension | Derived from fileName. _(ro)_ |
| `fileType` | File Type | Image / PDF / Word / Excel / …, derived from the extension. _(ro)_ |
| `parentNumber` | parentNumber | BC number of the record to attach to. Alternative to integrationId. Echoed on read. |
| `documentType` | Document Type | Attachment document type for document parents. _(ro)_ |
| `lineNo` | Line No. | Document line the attachment belongs to (0 = header level). _(ro)_ |
| `attachedDate` | Attached Date | When the file was attached. _(ro)_ |
| `lastModifiedDateTime` | SystemModifiedAt | _(ro)_ |
