Model Orders
Allocate many client accounts to an evergreen fund as a single block, with requirements evaluated in bulk and every account filled at the same price.
A Model Order places one order per account, for many accounts, as a single block. It is designed for advisor and model portfolio platforms, where one decision - adding a fund to a model, or rebalancing it - produces orders across a book of client accounts. Monark treats the orders in a Model Order as a block, and fills every one of them at the same price.
You build a Model Order in steps: create it as a draft, add the accounts' orders in one or more batches, then complete it. Model Orders work this way from API version 1.6.1. Earlier versions took every order in the create request.
If your platform places one order at a time, you do not need Model Orders. See Subscriptions (Buy Orders).
Endpoints used
| Method | Endpoint | Purpose |
|---|---|---|
POST | /primary/v1/omnibus/order-action/requirements/batch | Evaluate requirements for every account in one request |
PUT | /primary/v1/omnibus/order-action/requirements/{definitionId}/complete | Report a completed requirement for one account |
POST | /primary/v1/omnibus/model-order | Create a draft Model Order |
POST | /primary/v1/omnibus/model-order/investor-orders | Add a batch of per-account orders to a draft |
POST | /primary/v1/omnibus/model-order/complete | Submit a draft for processing |
GET | /primary/v1/omnibus/model-order | List Model Orders |
GET | /primary/v1/omnibus/model-order/{id} | Retrieve a Model Order with its per-account orders |
POST | /primary/v1/omnibus/model-order/cancel | Request cancellation of a Model Order and all of its orders |
How Model Orders are structured
| Level | Carries |
|---|---|
| Model Order (parent) | Fund, share class, side, currency, distribution election, tax lot election, routing, your referenceId, and your modelId |
| Order (one per account) | accountId, financialAdvisorId, its own referenceId, and an amount |
Every order inherits the parent's fund, share class, side, and elections. This has two consequences for how you translate a model into API calls:
| Scenario | Submit |
|---|---|
| A model allocates to several evergreen funds | One Model Order per fund. Send the same modelId on each, so they can be tied back to the model. |
| A rebalance buys for some accounts and sells for others | Two Model Orders: a Buy with dollar amounts, and a Sell with share quantities. |
Workflow
- The model strategist chooses the evergreen fund and share class to allocate to, and the target weight within the model.
- The advisor chooses the client accounts that will track the model.
- Your platform calculates each account's order amount from the account's value and the model's weights.
- Evaluate requirements for every account in one batch request, and collect what is outstanding. We recommend presenting all the suitability requirements for the evergreen funds in the model when the Advisor is allocating the client into the model.
- Create the Model Order, add the per-account orders, and complete it.
- Track the Model Order and its per-account orders.
Sizing each account's order is your platform's logic. Validate every calculated amount against the share class minimums before you submit, as described in Subscriptions (Buy Orders), and let the advisor exclude accounts that fall below them.
Evaluate requirements in bulk
POST /primary/v1/omnibus/order-action/requirements/batch
{
"omnibusFundId": "d4f6b8c0-0000-4000-8000-0000000000f1",
"omnibusShareClassId": "e5a7c9d1-0000-4000-8000-0000000000c1",
"side": "Buy",
"orders": [
{ "accountId": "b2d4e6f8-0000-4000-8000-000000000021", "financialAdvisorId": "c3e5a7b9-0000-4000-8000-000000000031" },
{ "accountId": "b2d4e6f8-0000-4000-8000-000000000022", "financialAdvisorId": "c3e5a7b9-0000-4000-8000-000000000031" }
]
}This request evaluates requirements only. It does not create or change anything.
{
"allSatisfied": false,
"orders": [
{
"accountId": "b2d4e6f8-0000-4000-8000-000000000021",
"financialAdvisorId": "c3e5a7b9-0000-4000-8000-000000000031",
"evaluationSucceeded": true,
"errorCode": null,
"errorMessage": null,
"allSatisfied": false,
"actions": [
{
"definitionId": "5c1d8a52-0000-4000-8000-0000000000d1",
"name": "Acknowledge Subscription Process",
"type": "TextAcknowledge",
"responsibleParty": "Advisor",
"status": "Required"
}
]
},
{
"accountId": "b2d4e6f8-0000-4000-8000-000000000022",
"financialAdvisorId": "c3e5a7b9-0000-4000-8000-000000000031",
"evaluationSucceeded": true,
"errorCode": null,
"errorMessage": null,
"allSatisfied": true,
"actions": []
}
]
}| Field | Description |
|---|---|
allSatisfied (top level) | true only when every account is satisfied. Gate the submit button on this. |
orders[] | One result per account, in the order you sent them. |
orders[].evaluationSucceeded | false when the account could not be evaluated. errorCode and errorMessage say why. |
orders[].allSatisfied | Whether this account has anything outstanding. |
orders[].actions[] | The account's requirements, in the shape described in Pre-Order Requirements. |
Important: Send each account's
financialAdvisorIdin the batch request, exactly as you will send it when you add the account's order. If the advisor is omitted, advisor-responsible requirements are not returned, the batch can reportallSatisfied: true, and the account's order is then refused withRequiredOrderActionsIncomplete.
Present requirements across accounts
Most requirements in a Model Order are identical across accounts. Showing the same disclosure once per account does not scale past a handful of clients, so group the results by definitionId and show one row per requirement, with a count of the accounts that have satisfied it.
| UI element | Source | Notes |
|---|---|---|
| Requirement | actions[].name, actions[].markdownText or actions[].actionText | Grouped by definitionId. |
| Responsible party | actions[].responsibleParty | |
| Progress | Accounts where the requirement is Satisfied ÷ accounts where it applies | For example, "2/3 accounts". |
| Outstanding total | Count of distinct requirements with at least one account Required | |
| Unevaluated accounts | orders[] where evaluationSucceeded is false | List separately, with errorMessage. Never count these as satisfied. |
When the advisor completes a grouped requirement, call the complete endpoint once for each account where it is Required, sending that account's accountId and financialAdvisorId. Then run the batch evaluation again.
Requirements whose responsibleParty is Investor or CoOwner are addressed to the client, not to the advisor. Agree with your compliance team whether they are routed to the client, or whether the advisor may attest to them under your firm's procedures. Where they are routed to the client, hold the account out of the Model Order until they are complete. Questionnaire requirements are answered per account, as described in Pre-Order Requirements.
Submit the Model Order
A Model Order is submitted in three steps:
| Step | Call | Result |
|---|---|---|
| 1. Create | POST /primary/v1/omnibus/model-order | 201. The Model Order is created with a status of Draft. |
| 2. Add orders | POST /primary/v1/omnibus/model-order/investor-orders | 200. Repeat for each batch of accounts. The Model Order stays Draft. |
| 3. Complete | POST /primary/v1/omnibus/model-order/complete | 202. The Model Order moves to Processing, and its outcome is sent by webhook. |
Complete the Model Order on the day you create it. A draft that is still open at 1:00 AM Eastern is cancelled, with a statusReasonCode of DraftExpired, and OmnibusModelOrderCancelled is sent. The time you complete the Model Order is its submission time, so complete it before Monark's cutoff for the dealing date you want.
1. Create the draft
POST /primary/v1/omnibus/model-order
IdempotencyKey: 1d9b7e52-3c44-4f0a-8e6b-5a2f9c7d1e30
{
"referenceId": "MOD-2026-000042",
"modelId": "Balanced Growth 60/20/20",
"financialInstitutionId": "7f3c2a10-0000-4000-8000-000000000001",
"omnibusFundId": "d4f6b8c0-0000-4000-8000-0000000000f1",
"omnibusShareClassId": "e5a7c9d1-0000-4000-8000-0000000000c1",
"side": "Buy",
"currency": "USD",
"distributionElection": "Drip",
"taxLotElection": "FIFO"
}| Field | Required | Description |
|---|---|---|
referenceId | Yes | Your identifier for the Model Order, up to 100 characters. Must be unique per partner. |
modelId | No | Your identifier for the model or rebalance, up to 100 characters. Shared across the Model Orders of one model. |
financialInstitutionId | Yes | The custodian that holds every account in the Model Order. Each account's owner and advisor must be at this custodian. |
omnibusFundId, omnibusShareClassId | Yes | The fund and share class every order trades. |
side | Yes | Buy or Sell. |
currency | Yes | USD. |
distributionElection | Yes | Cash or Drip. Applies to every order. |
taxLotElection | Yes | FIFO, LIFO, HCFO, or LCFO. Applies to every order. |
routeInstruction | No | TransferAgent (the default) or IntraCustodianNetting. Applies to every order. |
unnettedOrderRoute, nettingTif, nettingTifDate | No | Netting orders only. Apply to every order. See Subscriptions (Buy Orders). |
The draft is checked when you create it: the fund and share class, the custodian's access to the fund, and the omnibus account for the custodian, share class and distribution election. A refused draft is not stored. The response carries the draft's id, which the next two steps need.
{
"id": "8a4c2e60-0000-4000-8000-0000000000a1",
"externalId": "01a0b5c3e2f4d6a8b0c2",
"referenceId": "MOD-2026-000042",
"status": "Draft",
"submittedAtUtc": "2026-09-18T15:04:12.381Z",
"requestedRouteInstruction": null,
"routeInstruction": "TransferAgent",
"statusReasonCode": null,
"message": null,
"rejectedOrderReferenceId": null,
"fieldErrors": [],
"receivedOrderCount": 0
}2. Add the orders
POST /primary/v1/omnibus/model-order/investor-orders
{
"modelOrderId": "8a4c2e60-0000-4000-8000-0000000000a1",
"orders": [
{
"referenceId": "MOD-2026-000042-01",
"accountId": "b2d4e6f8-0000-4000-8000-000000000021",
"financialAdvisorId": "c3e5a7b9-0000-4000-8000-000000000031",
"notionalAmount": 50000
},
{
"referenceId": "MOD-2026-000042-02",
"accountId": "b2d4e6f8-0000-4000-8000-000000000022",
"financialAdvisorId": "c3e5a7b9-0000-4000-8000-000000000031",
"notionalAmount": 32500
}
]
}| Field | Required | Description |
|---|---|---|
modelOrderId | Yes | The draft's id. |
orders[] | Yes | One line per account. A Model Order holds up to 10,000 lines across all its batches, and an account can appear in it only once. |
orders[].referenceId | Yes | Your identifier for the account's order, up to 100 characters. Must be unique per partner, and can't already be on this or another open Model Order. |
orders[].accountId | Yes | The account. |
orders[].financialAdvisorId | Conditional | The account's advisor. Must match the batch evaluation. |
orders[].notionalAmount | For Buy | Dollars, greater than zero, up to 2 decimal places. Omit quantity. |
orders[].quantity | For Sell | Shares, greater than zero, up to 6 decimal places. Omit notionalAmount. |
Monark checks every line in the batch as it arrives: the account, its owner and advisor, accreditation, requirements, sellable shares for a Sell, the share class minimum for a Buy, and the custodian's per-order and daily limits. If any line fails, the whole batch is refused and none of it is added. The Model Order stays Draft and keeps the batches already added. The response names the check in statusReasonCode and, when one line caused it, the line in rejectedOrderReferenceId, so you can correct the line, or leave the account out, and send the batch again. For example, a batch refused with 409 because one account has open requirements:
{
"id": "8a4c2e60-0000-4000-8000-0000000000a1",
"externalId": "01a0b5c3e2f4d6a8b0c2",
"referenceId": "MOD-2026-000042",
"status": "Draft",
"submittedAtUtc": "2026-09-18T15:04:12.381Z",
"requestedRouteInstruction": null,
"routeInstruction": "TransferAgent",
"statusReasonCode": "RequiredOrderActionsIncomplete",
"message": "Individual order MOD-2026-000042-02 has incomplete required order actions.",
"rejectedOrderReferenceId": "MOD-2026-000042-02",
"fieldErrors": [],
"receivedOrderCount": 0
}An accepted batch returns 200 with the same body, and the reason fields are null. receivedOrderCount is the number of lines added to the Model Order so far. Send batches one at a time: two batches sent to the same draft at once can be refused with 409 (ModelOrderModifiedConcurrently).
3. Complete the Model Order
POST /primary/v1/omnibus/model-order/complete
{
"modelOrderId": "8a4c2e60-0000-4000-8000-0000000000a1",
"totalOrderCount": 2
}totalOrderCount is the number of lines you added across all batches. If it doesn't match receivedOrderCount, the request is refused with 409 (OrderCountMismatch) and the Model Order stays Draft, so a lost batch can't go unnoticed. Otherwise the response is 202 with a status of Processing. A Processing Model Order can't take more lines or be cancelled.
Monark then checks every line again, because holdings, limits and requirements can change after a batch is added, and creates the per-account orders. One account that fails a check now rejects the whole Model Order. The outcome is sent by webhook, and you can also retrieve the Model Order to see it:
| Outcome | status | Webhook |
|---|---|---|
| Accepted | Received | OmnibusModelOrderCreated. The per-account orders are created and listed in orders[]. |
| A line failed a check | Rejected | OmnibusModelOrderRejected. statusReasonCode names the check, and rejectedOrderReferenceId the line when one line caused it. No per-account orders are created. |
| Couldn't be processed | Failed | OmnibusModelOrderFailed, for example with ModelOrderProcessingFailed or SettlementPublishFailed. Contact Monark Operations before resubmitting. |
As with individual orders, branch on status and do not rely on the HTTP code alone.
Retries
Every step can be retried safely after a timeout, because a repeated call is refused rather than applied twice:
| Call | If the first call succeeded, the retry returns | What to do |
|---|---|---|
| Create | 409 (DuplicateReferenceId) | Find the draft with GET /primary/v1/omnibus/model-order?referenceId={referenceId}. |
| Add orders | 409 (DuplicateOrderReferenceId), naming a line that was already added | Retrieve the Model Order and check receivedOrderCount. |
| Complete | 409 (ModelOrderNotDraft), with the Model Order's current status in the body | Track the Model Order from that status. |
When a model spans several funds, you submit several Model Orders. Report the outcome of each one separately, so a rejected fund does not hide an accepted one, and so the advisor can correct and resubmit only what failed.
Track Model Orders
GET /primary/v1/omnibus/model-order?financialInstitutionId={financialInstitutionId}&modelId={modelId}
| Query parameter | Description |
|---|---|
financialInstitutionId | Model Orders for accounts held at one custodian. |
accountId | Model Orders that include the account. |
modelId | Model Orders submitted for one model. |
omnibusFundId, omnibusShareClassId | Model Orders for one fund or share class. |
side | Buy or Sell. |
statuses | One or more order statuses. |
referenceId, externalId | Look a Model Order up by your identifier or Monark's. |
Note: The list endpoint returns each Model Order without its per-account orders:
ordersis an empty array, which does not mean the Model Order has no accounts. Retrieve a single Model Order byidto get the account breakdown.
Note: The lines you add to a draft are not returned. Until the Model Order is processed,
ordersis empty on the detail endpoint too, and the amounts arenull.receivedOrderCountshows how many lines have been added, in every status, so keep your own record of each batch. A Model Order rejected while processing also has no per-account orders.
GET /primary/v1/omnibus/model-order/{id}
| UI element | Source | Notes |
|---|---|---|
| Model name | ModelOrder.modelId | The value you supplied. |
| Fund | Fund.name for ModelOrder.omnibusFundId | |
| Share class | ShareClass.symbol or ShareClass.series for ModelOrder.omnibusShareClassId | |
| Side | ModelOrder.side | |
| Status | ModelOrder.status | Uses the same lifecycle as individual orders, after Draft and Processing. |
| Notional | ModelOrder.notionalAmount | The sum of the per-account amounts, for a Buy. |
| Filled / remaining | ModelOrder.filledNotionalAmount, ModelOrder.remainingNotionalAmount | For a Buy, these add up to notionalAmount less order fees. Each account's feeAmount and effectiveNotionalAmount are on GET /primary/v1/omnibus/order/{id}. For a Sell: filledQuantity, remainingQuantity. |
| Accounts | Count of ModelOrder.orders, or ModelOrder.receivedOrderCount before it is processed | orders is returned by the detail endpoint only. receivedOrderCount is returned by both. |
| Submitted | ModelOrder.submittedAtUtc | |
| Account breakdown | orders[].accountId, orders[].externalAccountNumber, orders[].notionalAmount, orders[].status | Resolve account names as described in Accounts & Eligibility. |
| Rejection reason | ModelOrder.statusReasonCode, ModelOrder.rejectedStatusReasonMessage | ModelOrder.rejectedOrderReferenceId is the line that caused the rejection, when there is one. |
Model Orders raise their own webhook events, which mirror the individual order events: OmnibusModelOrderCreated, OmnibusModelOrderAccepted, OmnibusModelOrderRejected, OmnibusModelOrderSent, OmnibusModelOrderSettled, OmnibusModelOrderFailed, OmnibusModelOrderCancelled, and OmnibusModelOrderCancelRejected. OmnibusModelOrderPartiallyFilled is retired and never sent. Fills in the netting queue don't send an event of their own: read the block's progress (filledNotionalAmount, remainingNotionalAmount and so on) with GET /primary/v1/omnibus/model-order/{id}, and each account's fills with GET /primary/v1/omnibus/order/{id}. A block that fills completely in the netting queue moves to Settled without an event. The per-account orders do not raise OmnibusOrder events of their own, so track them through the Model Order events. The exception is a netting Model Order that reaches its nettingTif deadline: each per-account order then also sends OmnibusOrderSettled or OmnibusOrderCancelled. See Webhooks.
Fills
Every order in a Model Order is filled at the same NAV. When the transfer agent confirms the Model Order, the confirmed amount is allocated to each account's order pro rata to its amount, and every order moves to Settled together. If a fund prorates an oversubscribed repurchase offer, every account's sell fills by the same percentage and ends Settled, with the unfilled shares in remainingQuantity. Each Buy creates a tax lot in the account's position, and each Sell relieves lots according to the taxLotElection.
A Model Order routed to the netting queue nets as one order. Each fill is priced at that netting session's NAV (that day's NAV for a daily-NAV fund, or the last published NAV for a monthly-NAV fund) and split across the per-account orders pro rata to their remaining balances, so every account gets the same price on each fill. A block can fill over several sessions, and each fill creates its own tax lot. Any part that is not netted follows the Model Order's unnettedOrderRoute: it goes to the transfer agent at Monark's cutoff, or is cancelled at the netting deadline.
Each per-account order is also an order in its own right. It is returned by GET /primary/v1/omnibus/order with modelOrderId and modelId populated, so it appears in the account's own order history alongside orders placed individually. It can't be cancelled or changed on its own. See Portfolio & Positions.
Cancel a Model Order
POST /primary/v1/omnibus/model-order/cancel
{
"modelOrderId": "8a4c2e60-0000-4000-8000-0000000000a1"
}You can cancel a Model Order until Monark's cutoff, before it is sent to the transfer agent. Cancellation applies to every order within it. Once the Model Order is Executed, cancellation is refused with 409 (OrderNotCancellable). As with individual orders, cancellation is an asynchronous request: the response status is Accepted, Pending, or Rejected. Wait for the OmnibusModelOrderCancelled event, or retrieve the Model Order again, to confirm that it shows Cancelled. A Model Order routed to the netting queue can't be cancelled once submitted, and is refused with 409 (OrderNettingCancellationUnsupported).
A Draft is cancelled straight away: the response status is Accepted, and OmnibusModelOrderCancelled is sent. A Model Order that is Processing can't be cancelled (409, OrderNotCancellable). Wait for its outcome, then cancel it if it is Received.
| To do this | Do this |
|---|---|
| Withdraw the whole allocation | Cancel the Model Order. |
| Add accounts to a draft | Send another batch to investor-orders before you complete it. |
| Change amounts, or remove accounts | Cancel the Model Order, then submit a new one with the same modelId. Lines can't be changed or removed, even in a draft. |
| Cancel one account's order | Not supported through the individual order endpoints. Cancel and resubmit the Model Order without that account. |
Handling errors
Refusals from investor-orders leave the Model Order in Draft and add nothing from the batch. The same checks run again for every line after complete. A failure then rejects the whole Model Order with the same statusReasonCode, sent in OmnibusModelOrderRejected.
| HTTP | statusReasonCode | Cause | What to do |
|---|---|---|---|
409 | RequiredOrderActionsIncomplete | At least one account has outstanding requirements for the advisor sent on its order. | Run the batch evaluation again with the same advisors, and complete what is Required. |
400 | FinancialInstitutionFundAccessDenied | The custodian is not enabled for the fund. Nothing is stored. | Contact Monark. |
400 | InvestorFinancialInstitutionMismatch, FinancialAdvisorInvalid | An account's owner or advisor is at a different custodian from the Model Order. | Move the account to the right Model Order, or correct the advisor. |
400 | AccountInvalid | An account cannot trade - for example, an entity account with no active individual. | Fix or remove the account, and resubmit. |
403 | AccountAccreditationInvalid | An account's accreditation is missing, more than 12 months old, or NOT_APPLICABLE. | Update the accreditation, or remove the account. |
409 | AccountInvalid | An account is closed. | Remove the account, and resubmit. |
409 | DuplicateReferenceId | The Model Order referenceId has already been used. | Retrieve the original by referenceId instead of resubmitting. |
409 | DuplicateOrderReferenceId | An order line's referenceId has already been used. A duplicate within the same request returns 400. | Use new line references. |
409 | MaxOrderNotionalExceeded, MaxDailyNotionalExceeded, MaxOrderQuantityExceeded, MaxDailyQuantityExceeded | The Model Order exceeds a custodian's per-order or daily limit. Nothing is stored. | Split the allocation, or contact Monark. |
409 | OrderNettingCapabilityMissing | The Model Order was routed to the netting queue, but netting isn't enabled for this custodian, fund, and share class. Nothing is stored. | Resubmit with routeInstruction: TransferAgent, or contact Monark. |
400 | InvalidNettingInstructions | The netting deadline can't be set, for example a nettingTifDate in the past. Nothing is stored. | Choose a different nettingTif or date. |
409 | InsufficientSellableShares, SellHoldingsUnavailable | A Sell line exceeds the account's sellable shares, or holdings couldn't be verified. | Correct the quantity, or retry later. |
409 | OmnibusAccountNotFound | There is no omnibus account for the custodian, share class and distribution election. Returned when you create the draft. | Contact Monark. |
400 | InvestorFinancialInstitutionMissing | An account's owner has no custodian. | Fix or remove the account, and resubmit. |
400 | InvalidOrderAmount | A Buy line has no notionalAmount or has a quantity, or a Sell line the reverse. | Correct the line. |
400 | BelowMinimumInvestment, MinimumInvestmentHoldingsUnavailable | A Buy line is below the share class's initial minimum for an account with no position in it, or Monark couldn't check for a position. | Raise the amount or remove the account, or retry later. |
400 | OrdersExceeded | The Model Order would have more than 10,000 lines. | Split the allocation into several Model Orders. |
409 | DuplicateAccountId | The account was added in an earlier batch. A duplicate within one batch returns 400. | Leave the account out of the batch. |
409 | ModelOrderNotDraft | Lines were added to, or complete was called for, a Model Order that is no longer Draft. | Retrieve the Model Order. It may already be completed, or cancelled. |
409 | OrderCountMismatch | totalOrderCount doesn't match the lines received. The Model Order stays Draft. | Compare with receivedOrderCount, add any missing batch, and complete again. |
409 | ModelOrderModifiedConcurrently | Another request changed the draft at the same time. | Retrieve the Model Order and retry. Send batches one at a time. |
400 | - | Validation failed. The body is a validation problem whose errors map lists the messages for each field. | Correct and resubmit. |
When you resubmit after a rejection, use new referenceId values, because reference IDs must be unique per partner.
Next steps
- Show each account's orders and holdings: Portfolio & Positions
- Rebalance out of a fund: Redemptions (Sell Orders)
Updated 27 days ago