Migrate to the /v1/cefi paths
CeFi REST v1.0.6 removes the legacy /api/v1/ path prefix. Every operation
is now served only under /v1/cefi/.
This release is a path move only. Response bodies, status codes, error formats, pagination behaviour and authentication are byte-for-byte what they were — update your base path and you are done.
A second, separate change lands on November 1, 2026: schema version
v2024_01_01 reaches end of life and is removed. v2026_01_23 becomes the
only supported version, and it changes response shapes. Every client must be on
it by that date — see
Upgrading to v2026_01_23 below.
Are you affected?
You are affected if any request you send has a path beginning /api/v1/. Those
paths now return 404; there is no redirect and no grace period.
You are not affected if you were already calling /v1/cefi/ — the prefix
has been available since v1.0.5 (May 2026) and is unchanged.
Step 1 — change your base path
| Production | https://api.truex.co/v1/cefi/ |
| UAT (sandbox) | https://api.uat.truex.co/v1/cefi/ |
Most clients hold this in one constant. If yours does, that is the whole change.
Step 2 — re-check your HMAC signing
This is the step that is easy to miss. The signature covers the path:
signature = base64( HMAC_SHA256( secret, METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY ) )
If your client builds the signature from a hard-coded path string rather than
from the path it actually sends, changing the base URL alone will leave you
signing /api/v1/orders while requesting /v1/cefi/orders. That fails as
401 Unauthorized, not 404, which sends people looking in the wrong place.
Sign the request path you send.
JWT bearer clients (retail) have nothing to do here — there is no per-request signing.
Step 3 — map your paths
Every removed path and its replacement:
Service
| Removed | Use instead |
|---|---|
/api/v1/health | /v1/cefi/health |
/api/v1/versions | /v1/cefi/versions |
Assets and instruments
| Removed | Use instead |
|---|---|
/api/v1/assets | /v1/cefi/assets |
/api/v1/asset | /v1/cefi/assets |
/api/v1/instruments | /v1/cefi/instruments |
/api/v1/instrument | /v1/cefi/instruments |
Market data
| Removed | Use instead |
|---|---|
/api/v1/market/quote | /v1/cefi/market/quote |
/api/v1/markets/quote | /v1/cefi/markets/quote |
Clients
| Removed | Use instead |
|---|---|
/api/v1/clients | /v1/cefi/clients |
/api/v1/client | /v1/cefi/clients |
Orders
| Removed | Use instead |
|---|---|
/api/v1/orders | /v1/cefi/orders |
/api/v1/order | /v1/cefi/orders |
/api/v1/orders/{ref_id} | /v1/cefi/orders/{ref_id} |
/api/v1/order/{ref_id} | /v1/cefi/orders/{ref_id} |
/api/v1/orders/all | /v1/cefi/orders/all |
/api/v1/orders/active | /v1/cefi/orders/active |
/api/v1/order/active | /v1/cefi/orders/active |
/api/v1/orders/trade | /v1/cefi/orders/trade |
/api/v1/order/trade | /v1/cefi/orders/trade |
/api/v1/orders/activity | /v1/cefi/orders/activity |
/api/v1/order/activity | /v1/cefi/orders/activity |
/api/v1/orders/status/{ref_id} | /v1/cefi/orders/status/{ref_id} |
/api/v1/order/status/{ref_id} | /v1/cefi/orders/status/{ref_id} |
Balances
| Removed | Use instead |
|---|---|
/api/v1/balances | /v1/cefi/balances |
/api/v1/balance | /v1/cefi/balances |
/api/v1/balances/activity | /v1/cefi/balances/activity |
Transfers
| Removed | Use instead |
|---|---|
/api/v1/transfers | /v1/cefi/transfers |
/api/v1/transfer | /v1/cefi/transfers |
/api/v1/transfers/active | /v1/cefi/transfers/active |
/api/v1/transfer/active | /v1/cefi/transfers/active |
Singular forms are gone
The singular aliases (/asset, /instrument, /client, /order, /balance,
/transfer) were deprecated in v1.0.3 and are not carried over to /v1/cefi/.
Use the plural form — it is the same operation with the same response.
The one exception is market quotes, where /v1/cefi/market/quote and
/v1/cefi/markets/quote are both real endpoints with different behaviour. Keep
whichever one you were calling.
Step 4 — verify
curl -s https://api.truex.co/v1/cefi/health
Then grep your codebase for any remaining api/v1 — including config files,
deployment health checks, monitoring probes and integration test fixtures, not
just the client library.
What did not change in v1.0.6
Worth stating explicitly, because it is the most common wrong assumption about this release:
- The default schema version is still
v2024_01_01. A request with noX-Truex-Versionheader gets exactly the response it got before. - Response bodies are unchanged. Raw arrays and objects, not envelopes.
- Pagination is unchanged. List reads that were unpaginated stay unpaginated.
- Error bodies are unchanged. RFC 7807 Problem Details, unwrapped.
- Authentication is unchanged, apart from signing the new path.
Upgrading to v2026_01_23
On November 1, 2026, schema version v2024_01_01 is removed. From that
date:
v2026_01_23becomes the default, so a request sending noX-Truex-Versionheader gets the new response shapes.- A request that explicitly sends
X-Truex-Version: v2024_01_01is rejected.
This is a hard cutoff, not a default change you can sit out. v2024_01_01 is
already past its August 5, 2026 sunset.
Pinning X-Truex-Version: v2024_01_01 does not extend your deadline. Today
it holds the old format; on November 1 that same header starts failing your
requests outright.
If you pin it as a stopgap, set a reminder to remove it — a pinned
v2024_01_01 is the one configuration that turns this migration into an
outage rather than a change in response shape.
The work below is independent of the path move above, and you can start it today. Budget for it now: every client must be reading the envelope, following the cursor, and unwrapping errors before November 1.
Step 1 — adopt the new version
X-Truex-Version: v2026_01_23
Send this header and you get the new format immediately, on whatever date suits you, regardless of what the current default is. That is the whole opt-in — the rest of this section is the client-side work it requires.
The version may also be supplied as a query parameter if a header is inconvenient.
Pin v2026_01_23 explicitly rather than waiting to inherit it as the default on
November 1. An explicit header means you choose the date you absorb the change,
you can roll it out one environment at a time, and nothing about your client
changes when the default moves underneath it.
Step 2 — unwrap the envelope
Every response body is wrapped in data:
// v2024_01_01
[ { "...": "..." }, { "...": "..." } ]
// v2026_01_23
{
"data": [ { "...": "..." }, { "...": "..." } ],
"pagination": { "size": 10, "next_cursor": "1761955200000000000" }
}
If you want a decoder that works on both versions during the transition,
unwrap only when both keys are present — that is a no-op on v2024_01_01:
def unwrap(body):
if isinstance(body, dict) and "data" in body and "pagination" in body:
return body["data"]
return body
Step 3 — follow the cursor
This is the step that silently loses data if you skip it. Under v2024_01_01 a
list read returned everything. Under v2026_01_23 it returns at most size
records, and size defaults to 10.
A client that issues one request and treats the result as the complete set will read 10 orders and believe that is all of them. No error is raised.
| Page size parameter | ?size= |
| Default | 10 |
| Maximum | 100 (larger values are clamped, not rejected) |
| Cursor returned as | pagination.next_cursor |
| Cursor sent back as | ?timestamp= |
| End of results | next_cursor is null |
Note the asymmetry: the cursor comes back to you as next_cursor but goes
out as timestamp. It is a nanosecond-precision timestamp, and paging walks
backwards from it, so passing it through as an opaque string is correct — do not
parse, round, or reformat it.
def get_all(session, path, page_size=100):
results, cursor = [], None
while True:
params = {"size": page_size}
if cursor:
params["timestamp"] = cursor
body = session.get(path, params=params).json()
page = body["data"]
results.extend(page)
cursor = body["pagination"]["next_cursor"]
# An empty page alongside a cursor would loop forever — treat it as the end.
if not cursor or not page:
break
return results
Keep the not page guard. An empty page returned together with a non-null
cursor is the one shape that turns this loop into an infinite one.
Step 4 — unwrap errors
The RFC 7807 Problem Details body is nested under an error key. The fields
inside it are unchanged.
// v2024_01_01
{ "type": "...", "title": "...", "status": 400, "detail": "..." }
// v2026_01_23
{ "error": { "type": "...", "title": "...", "status": 400, "detail": "..." } }
Check your error handling for code that reads body["title"] or
body["detail"] directly — those move to body["error"]["title"] and
body["error"]["detail"]. HTTP status codes are unchanged, so any handling
keyed on the status code alone keeps working.
Your HMAC signature is not affected
Pagination adds query parameters, which raises a reasonable worry after
Step 2 of the path migration. It is not
a problem: the signature covers the path only, not the query string. Adding
?size=100×tamp=... does not change what you sign.
Sign /v1/cefi/orders whether you request /v1/cefi/orders or
/v1/cefi/orders?size=100×tamp=1761955200000000000.
Step 5 — verify
GET /v1/cefi/versions reports the supported versions and the current default,
so you can assert on it from a test instead of tracking this page.
A good end-to-end check: create more than 10 of something, then read the list back and confirm you get all of them. That single test catches the pagination mistake, which is the one that fails quietly.
Responses for a deprecated version carry RFC 8594 Deprecation and Sunset
headers — worth logging so an expiring version shows up in your own telemetry
before it starts failing requests.