> ## Documentation Index
> Fetch the complete documentation index at: https://docs.darknyx.trade/llms.txt
> Use this file to discover all available pages before exploring further.

> Replace a resting order atomically with an in-place cancel-and-replace, leaving no window where you hold neither order.

# Modify Order

<Info>
  **TL;DR**

  `PUT /orders/{order_id}` modifies a resting order as an **atomic cancel +
  replace**. The body carries a signed cancel of the old order plus a full,
  independently-signed replacement order. The swap happens under one lock with both
  preconditions checked first, so there is never a moment where you hold neither
  order.
</Info>

Auth: `Authorization: Bearer <token>` **and** two signatures (a cancel of the old
order and a full new order), both from the **same** trading key.

## Why modify instead of cancel-then-place

Cancelling and re-placing as two separate calls leaves a gap: between the cancel
landing and the new order arriving, you have *no* order resting, and a batch may
clear in that gap. `PUT /orders/{order_id}` closes the gap. It verifies both
sides, then applies the cancel and the replacement atomically: either the swap
happens whole, or nothing changes.

## Path parameters

| Parameter  | Type   | Description                                       |
| ---------- | ------ | ------------------------------------------------- |
| `order_id` | string | The 16-byte id (hex) of the OLD order to replace. |

## Request body

```json theme={null}
{
  "cancel_signature": "…",
  "cancel_nonce": "2",
  "replacement": { "… a full Place Order body …" }
}
```

| Field              | Type           | Required | Description                                                                                                                       |
| ------------------ | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `cancel_signature` | string         | Yes      | 64-byte hex. Ed25519 signature over the canonical cancel body of the OLD order, which proves ownership of what is being replaced. |
| `cancel_nonce`     | decimal string | Yes      | Canonical `u64` decimal string bound into `cancel_signature`; the string form is lossless in JavaScript.                          |
| `replacement`      | object         | Yes      | A complete, independently-signed [Place Order](./place-order.mdx) body. It carries its own collateral note and input proof.       |

The trading key that signs the cancel **must** be the key that signs the
replacement. The replacement may reuse the old order's note and proof while that
proof's root is still in the on-chain root window, or it may point at a different
note; it is a normal place-order body either way. Its `session_id` also scopes
the embedded cancel signature to the current boot.

The replacement must keep the original order's `symbol`. Atomic modify is an
operation inside one isolated market book; it cannot move intent between pairs.
To change markets, cancel the old order and place a fresh signed order.

### Reprice in place

If the replacement's `order_id` equals the path `order_id`, the modify is a
"reprice in place": the cancel frees the id and the replacement reclaims it, so
the logical order keeps its identity. If the replacement uses a new `order_id`,
the old id is retired and a `cancelled` event is emitted for it on the orders
stream.

## Example

```bash theme={null}
curl -s -X PUT "$GATEWAY/orders/$OLD_ORDER_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cancel_signature": "…",
    "cancel_nonce": "2",
    "replacement": { "symbol": "SOL-USDC", "side": "bid", "…": "…" }
  }'
```

## Success response

```json theme={null}
{
  "old_order_id": "aa00000000000000000000000000000001",
  "order_id": "aa00000000000000000000000000000002",
  "status": "modified",
  "arrival_slot": 309482140
}
```

| Field          | Type    | Description                                                       |
| -------------- | ------- | ----------------------------------------------------------------- |
| `old_order_id` | string  | The replaced order's id.                                          |
| `order_id`     | string  | The new order's id (equals `old_order_id` on a reprice in place). |
| `status`       | string  | `"modified"`.                                                     |
| `arrival_slot` | integer | The slot stamped on the replacement order.                        |

## Atomicity guarantees

Both preconditions are checked **before** anything mutates:

1. The old order exists and is owned by the signing trading key.
2. The replacement's `order_id` is not already booked (unless it equals the old
   id, the reprice-in-place case).

If either fails, the call returns an error and **neither** order is touched. Only
when both hold does the engine cancel the old order and book the replacement under
the same lock; no batch can clear between the two.

## Errors

| Condition                                                                   | Status |
| --------------------------------------------------------------------------- | ------ |
| Malformed fields / the replacement fails place-order verification           | `400`  |
| Missing or invalid bearer token                                             | `401`  |
| A signature does not verify, or the caller does not own the old order       | `403`  |
| The old order does not exist                                                | `404`  |
| The replacement's `order_id` is already booked, or a nonce/session is stale | `409`  |


## OpenAPI

````yaml api-reference/openapi/darknyx-public.yaml PUT /orders/{order_id}
openapi: 3.1.0
info:
  title: Darknyx TEE API
  version: 4.1.0
  description: |
    Public + authenticated REST and WebSocket API for the Darknyx dark pool
    TEE matching layer. One attested endpoint may expose several independently
    routed spot markets. Pairs with the v2 on-chain custody program
    (vault, program ID `C63vKvysCzX55PKraas4Wc22ijqjGJQdPC1mrzCFVWZx`).
  contact:
    name: Darknyx engineering
  license:
    name: PolyForm Perimeter License 1.0.1
    url: https://polyformproject.org/licenses/perimeter/1.0.1
servers:
  - url: https://api.darknyx.example.com
    description: Mainnet placeholder; use the origin published with a deployment.
  - url: https://api.devnet.darknyx.example.com
    description: Devnet placeholder; use the origin published with a deployment.
security: []
tags:
  - name: auth
    description: OAuth2 client-credentials and bearer-token lifecycle.
  - name: attestation
    description: |
      Darknyx engine and transport attestation plus the deployment gateway's
      separate evidence bundle. Programmatic clients verify the certificate on
      their actual connection with `/transport-attestation` before any
      credential or sensitive write. `/evidences/*` describes surrounding
      ingress infrastructure and is not a substitute for the engine check.
  - name: info
    description: |
      Application/instance metadata, boot-session id, and settlement signers.
      Verify measured identity through `/attestation`, not self-reported fields.
  - name: instruments
    description: Public market metadata.
  - name: orders
    description: Place / cancel / modify / inspect orders.
  - name: system
    description: Public engine liveness + server time (GTT slot conversion).
  - name: account
    description: Per-account open orders and preferences. Balances remain client-derived.
  - name: tree
    description: >-
      Convenience Merkle-tree mirror; clients can verify the same state on
      Solana.
  - name: transparency
    description: Public solvency snapshot + engine identity + aggregate stats.
  - name: settlement
    description: Batch settlement status (TEE → L1 tx_signature lookup).
paths:
  /orders/{order_id}:
    put:
      tags:
        - orders
      summary: |
        Modify an open order — atomic cancel + replace.
      description: >
        "The same owner replaces their resting order with a new one." The body

        carries a signed cancel of the OLD order (over its id) plus a full new

        order (`replacement`, a normal signed order). The trading key that signs

        the cancel MUST be the one that signs the replacement. The swap is

        applied under one matcher lock with BOTH preconditions checked first

        (old order exists + owned; replacement order_id not already booked
        unless

        it is the same id, a reprice in place), so there is no window where the

        caller has neither order. The replacement carries its own note +

        VALID_INPUT proof (it may reuse the old note while the root is still in

        the recent-root window) — no new signed message type; it composes the

        existing cancel + order canonical bodies. The replacement must use the

        original order's symbol: moving between isolated market books requires

        cancel + a fresh placement. Its session_id also scopes the embedded

        cancel signature to the current boot.
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: string
          description: The OLD order's id (hex) to replace.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ModifyOrderRequest'
      responses:
        '200':
          description: Modified — old order cancelled, replacement resting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModifyOrderResponse'
        '400':
          description: |
            Malformed request, cross-market replacement, or replacement
            place-order validation failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing / invalid / expired / revoked bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            Account suspended, or the booked order is not owned by this
            trading_key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: The old order doesn't exist.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            The replacement order_id is already booked, or a nonce/session is
            stale.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: >-
            New trading became paused while the replacement was verified; the
            original order is unchanged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - BearerAuth: []
components:
  schemas:
    ModifyOrderRequest:
      type: object
      required:
        - cancel_signature
        - cancel_nonce
        - replacement
      properties:
        cancel_signature:
          type: string
          description: 64-byte signature over the old order's canonical cancel body.
        cancel_nonce:
          type: string
          pattern: ^(0|[1-9][0-9]*)$
          description: Canonical decimal u64 string.
        replacement:
          $ref: '#/components/schemas/PlaceOrderRequest'
    ModifyOrderResponse:
      type: object
      required:
        - old_order_id
        - order_id
        - status
        - arrival_slot
      properties:
        old_order_id:
          type: string
        order_id:
          type: string
          description: Replacement id; may equal old_order_id for reprice-in-place.
        status:
          type: string
          enum:
            - modified
        arrival_slot:
          type: integer
          format: uint64
    Error:
      type: object
      description: |
        The error envelope. Every non-2xx response renders as this shape, with
        the mapped HTTP status. Success responses are NOT enveloped (their typed
        body is returned directly). Every response — success and error — carries
        an `x-request-id` header for correlation with server logs.
      required:
        - code
        - message
      properties:
        code:
          type: integer
          description: |
            Stable numeric error code. Ranges: 1000–1099 request validation,
            1100–1199 auth, 1200–1299 conflict, 1300–1399 not found, 1400–1499
            rate limit, 5000+ server. See the Error Codes reference.

            One exception to the ranges: `1402` is returned with HTTP 503, not
            429. It signals that credential verification is momentarily at
            capacity and was refused rather than queued. Branch on the numeric
            code rather than inferring the status from its range.
          example: 1102
        message:
          type: string
          example: trading_key_signature does not verify against the canonical body
    PlaceOrderRequest:
      type: object
      description: |
        The canonical-order wire schema (`darknyx-order-v5` signature domain).
        All 32/64/16-byte fields are hex-encoded strings; all numeric fields are
        JSON integers (u64).
        The trading-key signature covers the canonical encoding of the
        economic fields, `viewing_pubkey`, `session_id`, and `arrival_nonce`;
        the opening fields (`owner_commitment`, `note_inner_hash`) are pinned
        indirectly because the matcher re-derives
        `note_commitment` from them and rejects a mismatch.
      required:
        - symbol
        - side
        - order_type
        - amount
        - price_limit
        - expiry_slot
        - order_id
        - note_commitment
        - arrival_nonce
        - trading_key
        - trading_key_signature
        - owner_commitment
        - note_inner_hash
        - merkle_root
        - valid_input_proof
        - viewing_pubkey
        - session_id
      properties:
        symbol:
          type: string
        side:
          type: string
          enum:
            - bid
            - ask
          description: '`bid` = buy base; `ask` = sell base.'
        order_type:
          type: string
          enum:
            - limit
            - ioc
            - fok
        amount:
          type: integer
          format: uint64
          description: Order size in base units.
        price_limit:
          type: integer
          format: uint64
          description: |
            Worst acceptable price, in quote units per base. Required for
            a bid (must be > 0); an ask may use `0` to accept any clearing
            price. Every non-zero value must be an integer multiple of the
            instrument's `tick_size`.
        min_fill_size:
          type: integer
          format: uint64
          description: |
            Optional (default 0). Reject fills smaller than this; set equal
            to `amount` for resting all-or-none.
        expiry_slot:
          type: integer
          format: uint64
          description: |
            Future Solana slot past which this order auto-expires. It must
            leave the matcher settlement buffer and is bounded by the protocol
            maximum (currently 4,500 slots) to prevent an unbounded
            collateral-lock window. Zero is not a GTC sentinel.
        order_id:
          type: string
          description: 16-byte client-chosen id, hex. Must be unique + non-zero.
        note_commitment:
          type: string
          description: |
            32-byte hex — Poseidon6 commitment to the collateral note this
            order is backed by. The opening and VALID_INPUT proof are checked
            at intake on a settlement-enabled venue. The commitment can reserve
            at most one live or settlement-pending order in this venue; the
            on-chain lock remains the final authority at settlement.
        arrival_nonce:
          type: integer
          format: uint64
          description: |
            Nonce bound into the signed canonical body. After exact-body
            idempotency handling, it must be strictly greater than every prior
            accepted nonce for the same trading key.
        trading_key:
          type: string
          description: 32-byte hex — the Ed25519 public key that owns this order.
        trading_key_signature:
          type: string
          description: |
            64-byte hex — Ed25519 signature over `sha256(canonical body)`
            from the trading key. Lets the TEE attribute the order to a
            specific key without a per-order on-chain tx.
        owner_commitment:
          type: string
          description: |
            32-byte hex — the collateral note's owner commitment
            `Poseidon2(DOMAIN_OWNER_V2=32, spending_key)`. Part of the
            input-note opening the in-TEE VALID_MATCH_BATCH prover needs
            (the circuit re-derives `note_commitment` from the opening).
            This is the only NOTE-BOUND owner identity an order carries, and
            the one settlement derives output notes back to. Verified against
            the signed `note_commitment` at intake. Held in enclave memory
            only.
        note_inner_hash:
          type: string
          description: |
            32-byte hex — the collateral note's amount-independent
            `inner_hash`, which anchors both its commitment and unlinkable
            note-use tag.
        merkle_root:
          type: string
          description: |
            32-byte hex — the merkle root the `valid_input_proof` was
            generated against. A settlement-enabled venue checks its recent-root
            mirror before accepting the order; `lock_note` checks the
            authoritative on-chain window again at settlement.
        valid_input_proof:
          type: string
          description: |
            256-byte hex (`pi_a ‖ pi_b ‖ pi_c`) — the per-note VALID_INPUT
            Groth16 proof that gates `lock_note`. The TEE cannot generate
            it (needs the spending key + merkle witness), so the client
            supplies it. A settlement-enabled venue verifies it at intake
            against `(merkle_root, note_use_tag, collateral mint)`, deriving
            the tag from the supplied commitment + private inner hash;
            on-chain `lock_note` verifies it again. Enqueue-only simulator /
            load-test deployments do not settle and may deliberately accept
            stub proofs.
        collateral_amount:
          type: integer
          format: uint64
          description: |
            Optional over-collateralization: the actual value the
            collateral note carries when it exceeds the order's nominal
            cost. Lets a large note back a small order; the surplus returns
            as a change note. Omit for exact collateral.
        tree_id:
          type: integer
          format: uint8
          default: 0
          description: |
            Merkle-tree shard containing the collateral note. Intake rejects an
            out-of-range shard and checks the proof root against this shard's
            recent-root window before booking.
        viewing_pubkey:
          type: string
          description: |
            Required 32-byte contributory X25519 viewing-encryption public key,
            hex (`deriveViewingEncKeypair().publicKey`). The TEE
            encrypts each fill's `(trade, change)` output amounts to it and
            writes the ciphertext on-chain, so exact and partial output notes
            remain recoverable after a CVM redeploy. It is signed; low-order
            encodings that yield an all-zero shared secret are rejected.
        session_id:
          type: string
          pattern: ^[0-9a-fA-F]{64}$
          description: |
            Required 32-byte boot session id from `GET /info`, hex, bound into
            the signature. A stale session is rejected even when every other
            field and signature is valid. Programmatic clients confirm that the
            value matches the boot session bound by `/transport-attestation`;
            a substituted value causes rejection rather than stale-session
            acceptance.
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Short-lived (≤ 1h) bearer token from POST /auth/token.

        Expiry is EXACT — there is no grace period past `expires_in`, on REST
        or on the streaming transport. Refresh on a margin.

        A structurally valid, unexpired token is still refused when it has been
        revoked (401), when the operator has invalidated the tokens the account
        was holding (401), or when the account is suspended (403). Suspension
        also blocks issuing a new one, so re-authenticating does not clear it.

````