# Submitting and polling

Loading a file is asynchronous. Submitting hands Class a file to work through; the transactions arrive some time later. This page covers the round trip.

## The sequence

1. Build the data file and Base64-encode it — see [Structure and encoding](/products/class/apis/partner/loaded-files/structure).
2. `POST` the envelope to the business or fund route. Keep the `FileId` from the response.
3. Poll `GET …/loaded-files/{fileId}` until `ProcessResult` reaches a terminal value.
4. If any transactions failed or raised warnings, retrieve them from `…/loaded-files/{fileId}/processed-items`.


## Acceptance is not completion

A `200` on submit means the envelope and the decoded data file passed validation. It does not mean a single transaction has been loaded.

Everything that can be checked up front is checked at submit: the Base64 decodes, the JSON parses, the interface codes match, the batch size is within range, and each item has a unique `SourceTransactionId`. Anything that depends on the state of the data is checked later. A transaction naming a fund or holding account you cannot reach passes submit cleanly and fails during processing.

## Polling

Poll `ProcessResult`. It is the only field that tells you whether the file is finished.

| `ProcessResult` | Meaning |
|  --- | --- |
| `InProgress` | Still working. Keep polling. |
| `Successful` | Terminal. No transaction failed or raised a warning. |
| `PartiallySuccessful` | Terminal. Some transactions raised warnings — possibly all of them. |
| `PartiallyFailed` | Terminal. Some transactions failed outright. |
| `Failed` | Terminal. The file did not load. |


Use exponential backoff rather than a fixed interval.

Add `include=messages` to see the warnings and errors behind the result. Add `include=derived` as well to see the reason for a system error, if the file hit one:

```
GET …/loaded-files/{fileId}?include=messages&include=derived
```

Do not read `PartiallySuccessful` as "mostly worked". A file where no transaction succeeded can still
report it, with `Successful: 0`. The two partial values appear to track which count a transaction landed
in — `Warning` gives `PartiallySuccessful`, `Failed` gives `PartiallyFailed` — but neither tells
you how much of the file worked. Read the counts, not the word.

**Do not poll `Process`.** It names the pipeline phase the file is in — `Loading`, `Processing`, `Cancelling` — and files come to rest on `Processing` even after their result is final. A loop that waits for a completed phase will never exit.

## Reading the counts

The detail response carries a `Counts` object: `Total`, `Successful`, `InProgress`, `Warning` and `Failed`.

These count the transactions the pipeline created. They are **not** a count of the items you submitted, and the two can differ.

**Always compare `Total` against the number of items you sent, once `ProcessResult` is terminal.**

Be particularly careful with `BankAccount` references. A bank account that cannot be resolved
fails every transaction in the file, including transactions naming a valid holding by `Code`. An unknown
holding `Code`, by contrast, fails only its own transaction.

`Successful`, `Warning` and `Failed` add up to `Total`.

A file can report `Successful` with every count at zero and load nothing at all. When that happens there is no error on the submit response, nothing in `Messages`, nothing on processed-items, and the file shows as processed successfully in Class. `Total` against your own item count is the only signal you get.

The most common cause is a transaction date that falls in a period the fund has closed. Such transactions are discarded silently — they are not counted as failed, or warned about, or reported anywhere. They are simply absent.

That applies transaction by transaction, not just to whole files. A file of four items where one is dated in a closed period reports `Total: 3, Successful: 3`. Nothing indicates that a fourth item was ever submitted. Comparing `Total` against your own item count is the only way to notice.

`Successful` means the transaction was loaded, not that it has been reconciled. Bank statement lines arrive in Browse Bank Statements as unmatched, and expenses and income arrive as business events, also unmatched. Matching them is a separate step in Class.

Events that move no cash are the exception — a reinvested distribution or fee rebate, a spin-off, a share split — they arrive with no matching required.

## When a file completes with nothing in it

A file can complete with zero counts, no processed transactions and no transaction-level errors.

The reason, where there is one, is in `Messages`. If the file hit a system error, `DetailedSystemError` gives the reason instead. `derived` returns nothing for ordinary failures such as a transaction that does not balance.

If `Messages` is empty too, check the transaction dates first — see the note on counts above.

## Transactions that did not load

Where a failed transaction is reported depends on how far it got.

**A transaction that failed after its fund and holding were resolved** appears on processed-items
with `ProcessResult` of `Warning` or `Failed` and its `Errors` populated. This covers most
failures — an event the security does not support, a transaction that does not balance, a value
the pipeline rejects.

**A transaction whose fund could not be located** does not appear there at all. It never becomes a
processed item, and `Messages` reports the code and a count with no index. This is the case when
the `FundCode`, `BrandCode` or a `BankAccount` reference cannot be resolved.

A transaction whose fund resolved but whose *holding account* did not is still reported — it
appears with `ProcessResult: Failed` and `REFERENCE_LOCATOR2_UNABLE_TO_LOCATE_HOLDING_ACCOUNT`.

To find transactions whose fund could not be located, compare the `Index` values processed-items
returns against what you submitted. `Index` is the 1-based position of each transaction in the
`TransformedItems` array you sent.

For example, you submit three transactions and the third names a fund that does not exist.
`Messages` reports one `REFERENCE_LOCATOR_FUND_NOT_FOUND`, and processed-items returns only two
entries:

```json
"ProcessedItems": [
  { "Index": 1, "ProcessResult": "Successful", "Errors": [] },
  { "Index": 2, "ProcessResult": "Successful", "Errors": [] }
]
```

Index 3 is missing, so the third transaction is the one that failed. Entries are not always
returned in `Index` order, so sort them before comparing.

Looking the file up in Class gives you more for these cases: the Unprocessed tab lists the
identifiers of each transaction that could not be located, such as
`Fund:NOSUCHFUND-ZZZ;Brand:MYBRAND`.

There is no partial resubmission. To retry failed transactions, build a new file containing only
those transactions and submit it as a new request.

## Deleting a file

`DELETE …/loaded-files/{fileId}` does considerably more than stop a file that is still in flight.
It **reverses everything the file loaded**.

- Every transaction the file created is cancelled and disappears from the transactions and
business events screens.
- Any matching already done against those transactions is undone.
- The file record is removed from the list endpoint and from Browse Loaded Files in Class.
- `processed-items` returns empty afterwards.


The cancelled events remain visible in the business events audit in Class, marked cancelled, so
there is a trail even though the file record itself is gone.

This is the same action as the Delete button on the Loaded File Details screen in Class, and it
carries the same consequences. It is not a way to undo a submission cleanly.

Use it to stop a file you have just submitted in error. Do not use it on a file whose
transactions have been worked with, because the matching work goes with it.

The call returns immediately and the reversal happens asynchronously. Poll the detail endpoint
until `EffectiveStatusCode` is `Cancelled`.

**Watch the fields carefully here.** A deleted file reports `ProcessResult: Successful` and
`Process: Cancelling`. `EffectiveStatusCode` is the only field that tells you the file was
deleted — here is what a deleted file returns:

```json
"Process": "Cancelling",
"ProcessResult": "Successful",
"EffectiveStatusCode": "Cancelled",
"CancelledDate": "2026-09-20T15:02:48"
```

Deleting a file that is already deleted is accepted and returns its current state.

Requesting `?include=messages` on a deleted file returns `500`. Use the default poll.

## Retrying after a timeout

There is no idempotency key. Submitting the same content twice creates two files and loads the transactions twice.

If a submit request times out you cannot tell from the client whether the file was accepted. Before resubmitting, list the recent files and look for your `RequestId`:

```
GET /b/{businessCode}/loaded-files?fromDate=…&toDate=…
```

`RequestId` is returned on every row but is not a filter, so match on it yourself. If it is
there, the file was accepted and a resubmission would duplicate it.

`RequestId` is also what Class shows as the **File Name** on Browse Loaded Files, so a label that
means something to you and to your client is worth choosing.

## Finding files later

The list endpoint defaults to the last 30 days when `fromDate` and `toDate` are both omitted. Filter by `status` and `interfaceCode`, and page with `pageNumber` and `pageSize` up to 500.

List rows deliberately carry no counts. Use the detail endpoint when you need them for a specific file.

A file is stamped with the scope it was submitted on, not with the funds its transactions name. A file submitted at business scope is stamped at the business, so it will not appear in a fund-scope list even when every transaction in it belongs to that fund. Submit at fund scope if the file needs to be visible there.

Visibility also follows your access. With business-level access you see files stamped at the business and at any fund within it. With brand or fund-level access you see only files stamped at funds you can reach — which means a file you submit at business scope on a client's behalf may not be visible to them at all.