Errors & Reason Codes
The refusals a private markets integration has to handle, what each one means, and what the investor should do about it.
Integrating through Apex AscendOS?The product guidance on this page applies to you. The endpoints, fields and statuses are Monark's; use the AscendOS equivalents listed in Building on Apex AscendOS.
Most of what goes wrong in a private markets integration is not a bug — it is a business rule refusing a subscription for a reason the investor can act on. This page lists those refusals with their verbatim messages, so you can recognise them and say something useful rather than showing a generic error.
How Errors Are Returned
The private markets API returns a plain RFC 7807 problem response carrying an HTTP status and a human-readable message, and nothing else:
{
"detail": "Investment amount is lower than the minimum commitment amount. Min amount - 25000",
"status": 400
}Important: The private markets endpoints carry no machine-readable reason code. There is no
reasonCodeorstatusReasonCodefield to switch on. The HTTP status and thedetailstring are all you get, which is why the messages below are quoted verbatim — they are what your code has to recognise.
Show the message to the investor. It names the specific limit that was hit, and most of them are correctable without anyone contacting support.
Branch on the response body, not only on the HTTP status code. A 201 from a create endpoint means the request was processed; always read the body back.
Creating a Transaction
Request shape
These are caught before any business rule runs. They indicate a bug in your integration, not an investor problem.
| Message | Cause |
|---|---|
Specify exactly one of InvestorId or AccountId. | Both were sent, or neither. They are mutually exclusive. |
ExternalAccountNumber can only be used with InvestorId. | externalAccountNumber was sent alongside accountId. |
Must exclusively specify either Amount Shares or Amount Reserved Dollars. | Both were sent, or neither. |
Amount Reserved Dollars is required when Side is Subscription | A subscription was sent without a dollar amount. |
Transfer Direction can only be specified when Side is Transfer | transferDirection must be null on every other side. |
Side Transfer is only supported for RegisteredFund target asset type. | Transfers do not apply to SPVs. |
Only Subscription orders are currently supported. Side '<side>' is not supported. | An SPV transaction used a side other than Subscription. SPV positions cannot be redeemed. |
Field limits that will also refuse the request: amountReservedDollars must be greater than zero with at most 2 decimal places; amountShares greater than zero with at most 6 decimal places; referenceId and externalAccountNumber at most 100 characters.
Business rules
These are the ones your UI should anticipate and explain.
| Message | What it means | What the investor should do |
|---|---|---|
Investment amount is lower than the minimum commitment amount. Min amount - <amount> | Below the SPV's minCommitmentAmount, and more than the minimum still remains unallocated | Increase the amount to at least the minimum |
Investment amount is lower than the minimum commitment amount and not equal to the remaining dollar allocation. Remaining - <amount> | Less than the minimum is left in the raise, so the last investor has to take exactly what remains | Enter exactly the remaining allocation |
Investment amount is greater than the remaining dollar allocation. Remaining - <amount> | The raise cannot absorb the amount | Reduce the amount to the remaining allocation or below |
No seats remaining for the SPV. | The SPV has reached its 99-investor limit. It can be seat-closed while dollars remain | Nothing — the offering is closed to new investors. Show it as closed, not as an error |
Validate the first three in your own UI before submitting, using minCommitmentAmount and remainingDollarAllocation from the SPV. The seat limit can be anticipated from numberOfSeatsRemaining, but treat the response as authoritative.
Important: These rules are checked twice — once when the transaction is created, and again when its last action completes and the subscription is signed. A pending transaction consumes no allocation and no seat, so a subscription that was accepted at create time can still be refused on the way to
Processingif another investor filled the raise first. Handle a late failure, not only an immediate one: the transaction will land inFailed, and the cash you reserved has to be released.
Accreditation
A transaction is refused where the investor's accreditation is missing, expired or explicitly not applicable.
| Message | What it means |
|---|---|
Accreditation not set | No accreditation status has been recorded |
Accreditation has expired | The status is more than 12 months old. Accreditation must be refreshed annually |
Investor is not accredited | The status is NOT_APPLICABLE and the offering does not permit non-accredited investors |
NOT_SPECIFIED is the one status that keeps an investor usable without an accreditation date — it means "not collected yet" rather than "not accredited". See Accreditation.
Completing a Transaction Action
| HTTP | Message | Cause |
|---|---|---|
400 | Could not complete the action as the status is not pending. Current status: Complete | The action was already completed. Treat this as success and move on — it is the expected result of a double submit |
404 | — | The action does not exist, or its transaction belongs to another partner |
400 | Could not resolve the transaction account owners. | A questionnaire action could not resolve the investor behind the transaction |
A Questionnaire action refuses completion until its answers have been submitted. Post the answers to /primary/v1/questionnaire-answer first, then complete the action normally. The bulk route, PUT /primary/v2/transaction-action/transaction/{id}/complete-all, skips actions it cannot complete rather than failing the batch — so check the remaining actions afterwards rather than assuming the batch cleared them all.
Authentication
| HTTP | Cause |
|---|---|
401 | No Authorization header, an unrecognised key, or an expired key. Keys carry an expiry and stop working at it |
403 | The key authenticated but lacks the role for the endpoint |
404 | Frequently means "not yours" rather than "does not exist". A partner key can only see its own investors and their transactions, so another partner's record reads as missing |
Retrying Safely
POST requests accept an optional IdempotencyKey header, cached for 24 hours. Send one on every transaction submission, and reuse the same key when retrying after a timeout — the cached response is returned instead of a second subscription being created.
Send a unique referenceId as well. It is your own identifier rather than a retry guard, and it is what lets you find a transaction again: GET /primary/v2/transaction filtered by referenceId will tell you whether a submission landed, which is the fallback if a retry happens outside the 24-hour idempotency window.
Next Steps
- Take the whole flow end to end against the sandbox: Sandbox Walkthrough
- Confirm you handle each of these before production: Go-Live Checklist
Updated about 17 hours ago