Import issues
Rows the platform could not take in: a bank's holding, balance, contract or price that failed validation, a transaction that was rejected, and the notes the import raised. Each issue keeps the row as the bank delivered it and why it was rejected, so it can be corrected at the source, retried or discarded. Reconciliation differences are not import issues; they are reconciliation breaks. Reads need ingestion:read, writes ingestion:write. Every write takes ?dry_run=true and an Idempotency-Key header and is logged in the change history.
Data model
idstringA UUID.
kindstringinvalid_transaction,invalid_holding,invalid_balance,invalid_price,invalid_interest,invalid_basic_data,unmatched_holding,other,calc_invariant_failedorqc_note.custodian_idstring or nullThe bank that delivered the row.
as_of_datedate or nullThe row's day (a transaction's trade date).
statusstringopen,resolvedordiscarded.originstringlegacy(moved from the legacy platform) orv1. A legacy issue is read-only until your tenant has moved.validation_failuresarrayWhy the row was rejected:
[{ "field": "...", "error": "..." }].rawobjectThe rejected row as the bank delivered it, keys in snake_case. No legacy id is shown; the issue names its accounts, asset and custodian in its own fields.
cash_account_id, custody_account_id, asset_idstring or nullWhat the row is about.
created_at, resolved_attimestamp or nullRFC 3339, UTC, microseconds.
retry_requested_attimestamp or nullWhen the issue was last retried, as the retry recorded it; not normalised to the form of
created_at.
List import issues
Returns the issues you can see, oldest first, as a cursor list. An issue on a cash or custody account is seen by those who see that account; one on no account is seen by an admin if it is a rejected transaction, and by those who see its portfolio if it is a bank's row. An admin or Aleta sees every issue. An unknown parameter, or a kind or status outside the lists below, is a 400.
Query parameters
kindstringOne kind from the data model.
statusstringopen,resolvedordiscarded.cash_account_id, custody_account_id, custodian_idstringIssues of this cash account, custody account or bank. A UUID.
cursor, limitstring, integerThe page:
limitdefaults to 100; a larger value is capped at 1000.
curl -G https://api.aleta.ai/v1/import-issues \
-H "Authorization: Bearer {access_token}" \
-d status=open -d kind=invalid_holding
{
"data": [
{
"id": "019f95f0-2d40-7b58-8f6e-9a3c1d7e5b02",
"kind": "invalid_holding",
"custodian_id": "019dfeb0-0a11-7c2e-9f4b-1d2e3f4a5b6c",
"as_of_date": "2026-10-15",
"status": "open",
"origin": "legacy",
"validation_failures": [{ "field": "ISIN", "error": "Unknown ISIN" }],
"raw": { "isin": "DK0000000000", "nominal": "100", "currency_code": "DKK" },
"cash_account_id": null,
"custody_account_id": "019dfeb0-0511-77a7-a8be-a911ab988a0a",
"asset_id": null,
"created_at": "2026-10-15T05:02:11.000000Z",
"resolved_at": null,
"retry_requested_at": null
}
],
"next_cursor": null,
"limit": 100
}
Retrieve an import issue
Returns one issue. An unknown id, one you cannot see, or one that is not a UUID is a 404.
curl https://api.aleta.ai/v1/import-issues/019f95f0-2d40-7b58-8f6e-9a3c1d7e5b02 \
-H "Authorization: Bearer {access_token}"
Discard an import issue
Closes an open issue without taking the row in, and answers 200 with the issue; the row is kept as evidence. A discarded issue answers 200 unchanged; a resolved one is a 409 already_closed whose next_action is null: a closed issue is not opened again. Discarding takes a grant on each whole cash or custody account the issue is on; an issue on no account only an admin discards (403 otherwise). A legacy issue before your tenant has moved is a 409 legacy_managed. The checks run in this order: 404, 403, 409 legacy_managed, then the status.
Body parameters
commentstringOptional; goes to the change history.
curl -X POST https://api.aleta.ai/v1/import-issues/019f95f0-2d40-7b58-8f6e-9a3c1d7e5b02/discard \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"comment": "Duplicate of the corrected file."}'
Retry an import issue
Takes the row in again, at once. Retrying takes a grant on each whole cash or custody account the issue is on; an issue on no account only an admin retries (403 otherwise). A rejected price or interest (invalid_price, invalid_interest) is shared platform data that only Aleta retries (403 otherwise). It runs only once your tenant has moved to v1: before that every retry is a 409 legacy_managed. An issue that is not open is a 409 already_closed whose next_action is null. The checks run in this order: 404, 403, the body (400), 409 legacy_managed, then the status. The period lock is never bypassed.
What a retry does depends on the kind:
invalid_holdingfrom a bank's holding file: the holding is stored as the bank delivered it, as the custody account's holding on the issue's day, the issue becomesresolved, and the answer is200with the issue. A day on or before the closing date of the custody account's entity is a409 period_lockedwith the way to unlock. A holding that names a custody account or asset your tenant does not have is a422 unknown_holding_target; an issue with no dated holding to store is a422 no_holding. Sent again with the sameIdempotency-Key, a retry answers as the first call did; a resolved holding retried with a new key is a409 conflict.invalid_transaction: not taken in again through v1. The answer is a409 reprocessing_unavailablewhosenext_actionis the discard route: correct the transaction at the bank and import the file again, or discard the issue.invalid_price,invalid_interest: Aleta takes them in again in internal.aleta.io, so through v1 they are a409 reprocessing_unavailabletoo.- any other kind (
invalid_balance,invalid_basic_data, aninvalid_holdingfrom a contract,unmatched_holding,other,calc_invariant_failed,qc_note): never taken in again, a409 not_retryablewhosenext_actionis the discard route.
Body parameters
The body is empty or {}. No retry corrects the row or overrides a pipeline rule: a body that sets patch, skip_transaction_corrections, skip_recalculation or validation_rules_to_skip is a 400, on every kind and for every caller. ignore_transaction_lock, or any other field, is a 400 too.
curl -X POST https://api.aleta.ai/v1/import-issues/019f95f0-2d40-7b58-8f6e-9a3c1d7e5b02/retry \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{}'
{
"type": "https://docs.aleta.ai/developers/api/v1/errors#reprocessing_unavailable",
"title": "Conflict",
"status": 409,
"code": "reprocessing_unavailable",
"detail": "v1 cannot take a rejected transaction in again yet: correct it at the bank and import the file again, or discard the issue",
"next_action": {
"next": "discard",
"method": "POST",
"path": "/v1/import-issues/019f95f0-2d40-7b58-8f6e-9a3c1d7e5b02/discard",
"field": null
}
}