Skip to main content

Endpoint

TLS only. A plain ws:// connection is never answered: it hangs until your client gives up, with no error to read. Check the scheme before you debug anything else.
Production uses the same messages at a production URL, arranged with Securitize once your integration passes the go-live checklist.

Keys

You sign with an Ed25519 keypair. You register its public key with Securitize; the private key never leaves your systems, and nothing secret crosses the wire in either direction. Any Ed25519 keypair works, whatever chain your CETs are minted on; a Solana keypair is one. Prefer a dedicated keypair over one that also holds assets.

Generate a keypair

With OpenSSL, and Python’s base58 package (pip install base58) to print the keys the way the API uses them:
Send Securitize the base58 public key, the wallet that should receive your CETs, and whether the key trades or is read-only. To fund your cash with crypto, also send the wallets you will send USDC from. Never send the private key. Once the key is registered, the examples in Open a session connect with the private key in CET_SIGNING_KEY. To rotate, register another public key. Several can be active at once, so you can move to the new key and then ask Securitize to retire the old one, with no coordinated cutover. A key can be registered read-only: its session gets every read and every push, and place_order and cancel_order answer NOT_PERMITTED. Use one for monitoring and reconciliation, so the systems that only read never hold a key that can trade.

Signing

hello, place_order and cancel_order carry a timestamp and a signature:
  • payloadBytes are the exact bytes of payload as they go on the wire. The server verifies the payload it received, never a re-serialisation of it. Key order and whitespace are yours to choose, as long as what you sign is what you send: serialise the payload once, sign that string, and put the same string in the frame. This is the most common way a signed integration fails.
  • timestamp is Unix seconds, within 30 seconds of the server clock. Keep NTP running: a drifted clock has every signed message refused with TIMESTAMP_OUT_OF_WINDOW. welcome and pong carry serverTime to check yours against.
  • type is part of the signed text, so a signature made for one message cannot be lifted onto another.
Reads and the heartbeat ride on the session hello opens; everything that moves shares, CETs or cash is signed. Most reads are also served over HTTP without a session, each request signed with the same key: see Reads over HTTP. The sandbox-only sandbox_deposit and sandbox_withdraw are signed the same way.

Open a session

Connect, send a signed hello within 10 seconds, wait for welcome, then send ping every heartbeatSeconds. Both examples read the private key, base58, from an environment variable; a 64-byte Solana secret key works too, since its first 32 bytes are the Ed25519 seed.
welcome means you are connected. An error with SIGNATURE_INVALID means the signature or the key (below).

Limits

One socket is one session. A second connection opens a second session, not a second channel on the first.

Reconnecting

Reconnect on any close or dropped connection, including 1006 (closed without a close frame), with backoff, never in a tight loop, and open a new session. A release restarts the service and closes every socket with 1001. Updates sent while you were disconnected are not replayed: read back every order you still hold as working with get_order. See Track, cancel and reconcile orders.

When the session does not open

How sandbox behaves

Sandbox sends orders to the broker’s simulator, which executes every order at once, at whatever limit price you name, even one far from the market. That makes the whole lifecycle testable without a live market. Calibrate nothing on it:
  • A LIMIT order never rests. It fills at your price at once. In production it rests until the market reaches it.
  • Cancelling never succeeds. Every order has filled before the cancellation arrives, so every cancel_order answers ORDER_NOT_CANCELLABLE. In production, PENDING_CANCEL and CANCELLED are ordinary.
  • Before the market opens, an order priced more than 2% from the last price is refused. During market hours the simulator fills it regardless.
  • Fills come 100 shares at a time. A 250-share order arrives as three executions and three trade_updated frames, and usually mints more than once.
  • References must change between runs. An externalRef you already used is refused as a duplicate, including the ones in these docs.
  • You fund your own cash. The sandbox-only sandbox_deposit and sandbox_withdraw move USD into and out of it. See Sandbox funding.