InterposeInvest

Sandbox environment. Interpose is not a registered broker-dealer and holds no FINRA approval. Every API documented here is available in a simulated evaluation environment only — no client assets are held, no real trades are executed, and no custody is provided. The calculation engines are the production code paths; fills, custody, settlement and KYC decisioning are simulated. What is real and what is simulated →

Moving a book is two jobs, and only one of them is ours today.

Moving a book onto a new platform is two separate problems that get conflated constantly. One is moving records — who the accounts belong to, what the cost basis is, what happened in them historically. The other is moving assets, which is an ACATS transfer between regulated custodians. Interpose does the first with you today. It cannot do the second and will not before FINRA approval.

The first is also not a self-serve endpoint, and this page is not the “how to script your own migration” page you might be expecting. Bringing a book across is an assisted process we run with you during onboarding: you supply the position and lot export you already download from your custodian, we map it, we dry-run it, and we reconcile a pilot batch against your statements before anything else moves. What follows is what that actually involves, so you can scope your side of it.

What actually moves

StageWhat movesWho does it
Account holdersIdentities, with verificationYou, before anything else — the import creates no users and skips an account whose owner it cannot resolve
AccountsAccount record, type, cash balanceImport, run with us
Cost basisTax lots — acquisition date, price, quantityImport, run with us. This is the part that is hard, and it is its own section
HistoryThe transaction ledgerImport, run with us
PositionsCurrent holdingsNobody, by any working import path — see Current limits
The securities themselvesAssets and cashACATS between custodians. Not available — Interpose holds no FINRA approval and cannot receive a transfer

Anything in the API that looks like it moves assets does not

There is an asset-transfer surface that models the ACATS lifecycle. It is a workflow simulation for evaluation. No code path in the platform moves real securities or cash, and nothing in this section changes that until the broker-dealer approval lands.

How a migration runs

The import endpoints are real and they are the ones we drive. They are also operator-gated: they mint active brokerage accounts and append to the immutable transaction ledger, so they require a credential we issue rather than the advisor login your sandbox signup returns. A portal login or a self-serve API key gets 403 on every BaaS path in this section. That is deliberate, not a permissions gap to route around. The Portfolio Management import routes below accept an ordinary advisor login — they build portfolios and models rather than minting accounts or writing to the ledger.

POST/api/v1/baas/migration/accounts/batch
POST/api/v1/baas/migration/tax-lots/batch
POST/api/v1/baas/migration/transactions/batch
GET/api/v1/baas/migration/jobs/{job_id}
GET/api/v1/baas/migration/jobs/{job_id}/errors

Every one of them takes canonical JSON — one documented record shape, whatever your data came from. There is no CSV parser and no file-upload path, so converting the custodian export into that shape is the work, and it is the part where an engagement earns its keep. Batches run inline and come back with a job that is already terminal, so polling is optional; the errors endpoint returns per-row failures indexed against your submitted array.

the three flags that decide whether a run is safe
dry_run          # true first, always — validates every record, persists nothing
idempotency_key  # mandatory in practice on accounts and lots; a bare re-run duplicates them
as_of_date       # your cutover date, on the tax-lot batch — see below

as_of_date is not optional in any real sense

Omit it and each lot's holding period is derived against today rather than your cutover, which makes long-term versus short-term classification depend on the afternoon you happened to run the import.

idempotency_key is not bound to the payload, either. Re-using a key with different data returns the original job and imports nothing — success-shaped, and a no-op. To retry a partial batch, use a new key with only the failed rows.

The Portfolio Management side

Your model library, portfolios and prior return series come across separately, on Portfolio Management's own import surface:

POST/api/v1/pm/migration/pm/models/batch
POST/api/v1/pm/migration/pm/portfolios/batch
POST/api/v1/pm/migration/pm/performance/historical
POST/api/v1/pm/migration/pm/performance/composites

The repeated pm segment is real, not a typo. Those routes declare /v1/migration/pm/… on the service, and the proxy rewrites /api/v1/pm/… to /v1/… — so the pillar segment and the route's own segment both survive into the public path. The same thing happens on PM's WebSocket path.

Cost basis is the part that hurts

Account numbers and balances are easy to move. A client's acquisition dates and lot-level basis are what determine their tax bill, and a migration that approximates them creates a problem you will be explaining to clients for years. So the honest split:

FieldWhy it matters
acquisition_dateDecides long-term versus short-term treatment. A substituted date makes a fifteen-year holding read as short-term, and it will not announce itself — it surfaces as an unexpected short-term gain in a year-end report.
acquisition_priceThe basis itself. 4dp string, per the platform's money rules.
remaining_quantityYou must supply the post-sale value. An imported sell does not deplete an imported lot — lot matching runs inside the live trading path, which the importer bypasses. Import a 100-share lot and a 40-share sale and the lot still reads 100.

Reconstructing lots from a real buy history is accurate. Anything else is an approximation, and if your export has no buy history for a position the only available move is to use average cost as the price and the cutover date as the acquisition date. If that is where you end up: flag every approximated lot so it is distinguishable from a real one, correct it before the position is sold, and tell the advisor which positions are affected. An extra week extracting real acquisition data from the incumbent is cheaper than all three.

What we cannot tell you in advance

Two things are genuinely unknown until we look at your export, and we would rather say so than guess. The first is whether your custodian gives you lot-level acquisition dates at all — some exports carry them, some carry only an average cost and a position-level open date, and which one you have decides how much of the work is data recovery rather than mapping. The second is wash-sale and covered/non-covered status: the import accepts is_covered and a wash-sale adjustment per lot, but it carries across what you send and does not reconstruct either from history. If your incumbent does not report them per lot, that gap comes across with the book, and closing it is a decision to make deliberately rather than a field to fill in.

Verification is yours, and it is not optional

There is no post-migration reconciliation endpoint, no checksum comparison and no completion event. The job record and its error list are the only feedback the platform gives you, so reconciliation is a step in the plan rather than a thing the software does. For every migrated account, against the incumbent's closing statement at cutover: cash balance, lot count, total cost basis, summed lot quantities against the position report, and transaction count.

Do that on ten accounts before four hundred. The cost of finding a mapping error at ten accounts is an afternoon; at four hundred it is a data-correction project with client statements attached to it.

Current limits

what is not built yet

Records import today and we run it with you. These are the parts that do not work the way the endpoint names suggest, stated so you design around them rather than discover them mid-cutover.

  • Positions are not populated by any working import path

    The transaction importer writes historical records without replaying them through the trading engine — by design, so that re-deriving cash balances cannot overwrite the custodian's reported figure. The consequence is that imported transactions open no lots, update no positions and recompute no buying power.

    There is an endpoint that looks like it closes this gap. It does not: it fails on every real call, and because a dry run short-circuits before the failure, the dry run reports success. Do not build on it. Plan for holdings to be established another way, and do not promise a ready-to-trade account at cutover.

  • Imported sells do not deplete imported lots

    Compute net remaining_quantity per lot in your mapping step, before import. Nothing downstream will do it for you, and a lot left at its original quantity overstates the client's basis silently.

  • Accounts and tax lots are not idempotent by default

    A bare re-run creates duplicates. Always send an idempotency_key, and never re-use one with changed data — the key is not bound to the payload, so that combination returns the original job and imports nothing.

  • Job state does not survive a restart

    Migration jobs are held in memory, so job_id is not durable and the external_id → account mapping is session-scoped. Capture the returned Interpose account ids immediately; a later batch keyed by your incumbent's identifier can fail wholesale after a restart. Chunk at roughly 500–1,000 records — requests are processed inline and are not resumable.

  • The adapter list names formats that have no parsers behind it

    An endpoint lists custodian format names, and the incumbent_format field is recorded on the job for your audit trail. Neither changes how anything is parsed — canonical JSON is the only input shape. Treat that list as labels, not capabilities.

  • The migration endpoints are not in the published OpenAPI specifications

    They exist, they are operator-gated, and they are not described in the specs you can download. That is one more reason this runs as a conversation rather than as something you generate a client for.

  • Assets cannot move at all

    Interpose is not a registered broker-dealer, holds no FINRA approval, and cannot receive an ACATS transfer or take custody. Records and assets are separate problems on separate timelines, and anything describing otherwise — including a document with “migration” in its title — is describing a future state.

Starting one

Tell us what you are moving from and roughly how many accounts, and we will tell you what your export needs to contain and what the sequencing looks like for your situation. It is a real conversation with a real answer; it is just not an API call. Ask us.