EdiphyAPI Documentation

EMS Client API Reference

Overview

The Ediphy EMS Client API provides programmatic access to our execution management system, enabling you to seamlessly integrate order submission and management into your existing trading infrastructure.

Initial Product Coverage: IMM Swaps (IRS)

Why the Ediphy EMS API?


1. Key Concepts

1.1 Connection Heartbeat & Safety

All order-management connections must maintain an active heartbeat. This is a safety mechanism to protect clients from runaway orders if they lose control of their connection.

Heartbeat Requirements:

Auto-Cancel on Heartbeat Failure:

Rationale

This safety mechanism prevents scenarios where a client submits orders, loses connectivity, and those orders continue trading without oversight. Most institutional trading systems implement similar protections.


1.2 Eventual Consistency Model

The EMS operates on an eventually consistent model:

  1. Client submits a request (create, amend, cancel) describing the desired state
  2. The API persists the request and queues it for processing
  3. The EMS processes the latest request for each order when ready
  4. Client polls order state to observe changes
  5. Order state includes completed flag (true = no further market exposure) and validation_error field
Important

The EMS does not acknowledge individual requests. Multiple rapid requests for the same order will "collapse" - only the latest desired state is processed. Clients must monitor order state to understand outcomes.


1.3 Trading Session

A trading session is defined as the UTC 24-hour day period:

Session boundaries affect: - Request log retention: Requests are retained for the current session only - Idempotency-Key cache: The header-based idempotency cache resets at session start - Order listing: GET /orders returns orders from the current session

Orders created in one session may remain active across session boundaries (e.g., GTC orders), but administrative data (request log, Idempotency-Key cache) resets at 00:00 UTC.

Cross-session order behavior: If a GTC order is created at 23:59 UTC and the client reconnects at 00:01 UTC the next day: - The order is still active and can be queried via GET /orders/{order_id} - The order appears in GET /orders for the new session - The original creation request is not available via GET /requests (request log was cleared) - The client_order_id cannot be reused — it remains reserved by the active GTC order until that order completes - The fills array will be empty — fills from previous sessions are not carried over. Clients requiring historical fill data should persist fills locally or request audit reports (see Section 11.4).


2. Authentication & Authorization

2.1 Authentication Model

The API supports two authentication methods: Bearer Token for standard integrations and Mutual TLS (mTLS) for clients requiring stronger security guarantees.

Bearer Token Authentication (Standard)

API keys are generated and managed through the Ediphy client portal. Include the API key in the Authorization header:

Authorization: Bearer <api_key>

Mutual TLS Authentication (Enhanced Security)

For clients with stricter security requirements (regulatory compliance, enhanced audit trails, defense in depth), we support mutual TLS (mTLS) authentication.

How it works:

  1. Client generates an X.509 certificate and private key
  2. Client registers the certificate (or CA) via the Ediphy client portal
  3. TLS handshake authenticates both client and server
  4. No Authorization header required — identity established at transport layer

Benefits:

How mTLS works with the API:

The API uses a single set of endpoints for both authentication methods. The server requests a client certificate during TLS handshake but does not require one:

This allows clients to choose their authentication method on a per-connection basis without changing endpoints.

Note

Contact support@ediphymarkets.com for assistance with mTLS certificate registration and setup.

2.1.1 API Key Management

API keys are managed through the Ediphy client portal:

Key rotation workflow: To rotate credentials safely, use the following client-side workflow: 1. Generate a new key (old key remains active) 2. Deploy the new key to your systems 3. Revoke the old key once all systems are using the new credentials

This approach gives you full control over the rotation timing and ensures no service interruption.

Active connections: When a key is revoked, any active REST requests using that key will fail with 401 Unauthorized. WebSocket connections authenticated with the revoked key will receive a disconnecting message and be closed. Clients should reauthenticate with the new key.

2.2 Authorization Model

Permissions are hierarchical and additive:

client.<client_id>
├── products
│   ├── irs.imm              # Can trade IMM swaps
│   ├── irs.spot             # Can trade spot swaps
│   ├── irs.forward          # Can trade forward starting swaps
│   ├── bonds.govt           # Can trade government bonds
│   └── bonds.credit         # Can trade credit bonds
├── currencies
│   ├── USD
│   ├── EUR
│   ├── GBP
│   └── JPY
├── actions
│   ├── order.create         # Can create orders
│   ├── order.cancel         # Can cancel orders
│   └── order.query          # Can query order status
├── limits
│   ├── max_notional: 500    # Max notional per order (MM)
│   ├── max_daily: 5000      # Max daily notional (MM)
│   └── max_open_orders: 50  # Max concurrent open orders
└── allocations
    ├── FUND_ABC             # Allowed allocation targets
    └── FUND_XYZ

2.3 Permission Validation

The gateway validates all requests against your configured permissions before forwarding to the EMS. If a request fails validation, you receive a 403 Forbidden response with a specific error code indicating which permission check failed (product, currency, action, allocation, or limit).


3. API Design

3.1 Base URL Structure

Production:  https://api.ediphymarkets.com/ems/v1
Sandbox:     https://api.sandbox.ediphymarkets.com/ems/v1

3.2 Common Headers

Request (Bearer Token):

Content-Type: application/json
Authorization: Bearer <api_key>
Idempotency-Key: <uuid>           # Required for POST/PATCH requests

Request (mTLS):

Content-Type: application/json
Idempotency-Key: <uuid>           # Required for POST/PATCH requests

No Authorization header required — client identity established via TLS certificate.

Response:

Content-Type: application/json
X-Ediphy-Request-Id: <uuid>       # For support/debugging
X-Ediphy-RateLimit-Remaining: 98

Idempotency-Key Header

The Idempotency-Key header is required for all POST and PATCH requests. This ensures network safety — if a request times out or fails ambiguously, the client can safely retry with the same key.

Important

`Idempotency-Key` and `client_order_id` serve different purposes. The header provides network-level retry safety; the body field provides business-level order identification. See Section 4.1 for details.

3.3 Error Response Format

JSON
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid quantity: must be positive", "field": "quantity", "request_id": "req_abc123" } }

Error codes:

Example Error Responses

Permission Denied (403):

JSON
{ "error": { "code": "PERMISSION_DENIED", "message": "Client not authorized for product: irs_imm with currency JPY", "request_id": "req_xyz789" } }

Validation Error (400):

JSON
{ "error": { "code": "VALIDATION_ERROR", "message": "Quantity exceeds maximum allowed: 500MM", "field": "quantity.value", "request_id": "req_abc456" } }

Duplicate Client Order ID (409):

JSON
{ "error": { "code": "DUPLICATE_CLIENT_ORDER_ID", "message": "client_order_id 'my-order-12345' is already in use by active order ord_7f3a9b2c", "field": "client_order_id", "request_id": "req_def123", "existing_order_id": "ord_7f3a9b2c" } }

3.4 HTTP Status Codes

Status Code Meaning When Used
200 OK Success GET requests returning data
202 Accepted Request accepted for processing POST/PATCH for orders (async processing)
400 Bad Request Malformed request Invalid JSON, missing required fields
401 Unauthorized Authentication failed Invalid/missing credentials, expired token
403 Forbidden Permission denied Valid auth but not authorized for action
404 Not Found Resource not found Unknown order_id, request_id
409 Conflict Conflict with current state Duplicate client_order_id
429 Too Many Requests Rate limit exceeded See rate limiting section
500 Internal Server Error Server error Unexpected gateway failure
502 Bad Gateway Upstream error EMS connection failure
503 Service Unavailable Service unavailable Maintenance or overload

4. Order Endpoints

4.1 Create Order

POST /orders

Creates a new order. Supports market orders (no limit) and limit orders.

Request Body

JSON
{ "instrument": { "type": "irs_imm", "currency": "USD", "imm_date": "H25", "tenor": "10Y" }, "side": "pay", "quantity": { "notional": 50000000 }, "limit": { "rate": 4.25 }, "time_in_force": "GTT", "expire_at": "2025-01-15T16:00:00Z", "allocation": "FUND_ABC", "client_order_id": "my-order-12345", "price_protection": "market", "metadata": { "trader": "jsmith", "desk": "rates-nyc" } }

Field Definitions

Field Required Description
instrument Yes Instrument specification (see below)
side Yes "pay" or "receive" for IRS; "buy" or "sell" for bonds
quantity Yes Quantity specification (see below)
quantity.notional Yes* Absolute notional value in base currency units, not millions (e.g., 50000000 for 50MM USD)
quantity.dv01 Yes* DV01 in USD. See DV01 section below.
quantity.notional_cap No Maximum notional (in base units) when using DV01 specification. Only valid with dv01. If the DV01→notional conversion would exceed this cap, the order is rejected with NOTIONAL_CAP_EXCEEDED. Recommended for risk control.
limit No Price limit. Omit for market order. If omitted with price_protection: "off", the order executes as a classic market order with no price constraints.
limit.rate If limit For IRS: swap rate (e.g., 4.25)
limit.price If limit For bonds: clean price (e.g., 99.5)
limit.yield If limit For bonds: yield (e.g., 4.15)
time_in_force No "GTT" (good-till-time, default), "GTC" (good-till-cancel), "EOD" (end-of-day)
expire_at If GTT ISO8601 expiry time. Mutually exclusive with time_in_force: "GTC" or "EOD" — providing both is a validation error.
allocation Yes Fund/allocation target ID (must be in client's allowed list)
client_order_id Yes Client's business reference for the order (1-64 characters, alphanumeric, hyphens, underscores: a-zA-Z0-9_-). Must be unique among all active (non-completed) orders. See below for uniqueness semantics.
price_protection No Controls how far from the current market price the order is allowed to execute. Named levels in order of most to least protection: "passive" (tightest), "market" (default), "aggressive", "off" (no protection). The actual basis point thresholds for named levels are dynamic, calculated from live market data and conditions. Alternatively, specify a numeric value for explicit basis point tolerance (e.g., 5 for 5bps).
metadata No Free-form JSON object for client use (logged but not processed). Maximum size: 4KB. Maximum nesting depth: 3 levels.

*Quantity must specify exactly one of notional or dv01.

Idempotency and Order Identification

Order creation uses two complementary mechanisms for safety:

Idempotency-Key Header (Network Safety)

The Idempotency-Key header (see Section 3.2) ensures safe retries at the network level:

client_order_id Field (Business Logic)

The client_order_id provides a meaningful business identifier for the order:

Why both?

The `Idempotency-Key` handles transient network failures (retry the same request safely). The `client_order_id` prevents business-logic errors (accidentally creating duplicate orders with the same reference). A GTC order created Monday remains active Tuesday — the `Idempotency-Key` cache resets, but the `client_order_id` collision check protects you from accidentally duplicating the order.

DV01 Quantity

When using dv01 to specify quantity, the gateway converts the DV01 value to notional using the instrument's current DV01 factor. This factor varies by instrument (tenor, currency, rate environment).

Request format:

JSON
{ "quantity": { "dv01": 5500, "notional_cap": 50000000 } }
Field Required Description
dv01 Yes Target DV01 in USD
notional_cap No Maximum notional (in base units) the client is willing to trade. If the conversion would exceed this cap, the order is rejected with NOTIONAL_CAP_EXCEEDED. Recommended for risk control.

Conversion timing: The DV01→notional conversion is performed at order submission time. The DV01 factor used is locked when the gateway accepts the order, ensuring the notional amount is fixed regardless of subsequent market movements.

Response includes conversion details:

The order response and state will include the conversion factor used and the resulting notional:

JSON
{ "goal_quantity": { "dv01": 5500, "dv01_factor": 8750, "notional": 48125000 } }

Instrument Specifications

IRS IMM Swap:

JSON
{ "type": "irs_imm", "currency": "USD", // USD, EUR, GBP, JPY "imm_date": "H25", // IMM code: H/M/U/Z + YY "tenor": "10Y" // 1Y, 2Y, 3Y, 5Y, 7Y, 10Y, 12Y, 15Y, 20Y, 25Y, 30Y }

IRS Spot Swap:

JSON
{ "type": "irs_spot", "currency": "EUR", "tenor": "5Y" }

IRS Forward Starting:

JSON
{ "type": "irs_forward", "currency": "GBP", "start": "1Y", "tenor": "10Y" }

Government Bond:

JSON
{ "type": "govt_bond", "isin": "US912828ZT77", "maturity_date": "2030-05-15" }

Credit Bond:

JSON
{ "type": "credit_bond", "isin": "US037833EK23", "maturity_date": "2028-02-01" }

Response (Success - 202 Accepted)

JSON
{ "request_id": "req_abc123", "order_id": "ord_7f3a9b2c", "client_order_id": "my-order-12345", "status": "sent_to_ems", "message": "Request sent to execution system. Poll order state for updates.", "submitted_at": "2025-01-15T10:30:00.123Z", "links": { "order": "/orders/ord_7f3a9b2c", "request": "/requests/req_abc123" } }
Note

`202 Accepted` means the gateway has accepted and forwarded the request. It does **not** mean the order is working or that you have market exposure. Poll `GET /orders/{order_id}` to observe actual order state.


4.2 Amend Order

PATCH /orders/{order_id}

Submits an amendment request for an order. The EMS will process the latest desired state.

Amendable Fields

Field Description
limit Update price limit
quantity Update goal quantity
expire_at Extend or shorten expiry time
price_protection Change price protection level
Note

Fields not listed above cannot be amended. To change instrument, side, or allocation, cancel and submit a new order.

Request Body

JSON
{ "limit": { "rate": 4.30 }, "expire_at": "2025-01-15T18:00:00Z" }

Response (Success - 202 Accepted)

JSON
{ "request_id": "req_def456", "order_id": "ord_7f3a9b2c", "status": "sent_to_ems", "message": "Amendment request sent to execution system. Poll order state for updates.", "submitted_at": "2025-01-15T10:33:00.123Z", "links": { "order": "/orders/ord_7f3a9b2c", "request": "/requests/req_def456" } }
Note on amendment behavior

**Quantity amendments:** If you amend quantity to 30MM but the order has already filled 35MM, the amendment is accepted (not an error) and the order will show filled 35MM with `completed: true`. This is normal behavior — quantity adjustments and fills occur asynchronously, and it's common for fills to outpace amendments during active trading. **Price amendments:** When amending the limit price on a partially filled order, the new price applies only to the remaining unfilled quantity. Previously executed fills retain their original prices.

Atomic processing: The EMS processes amendments atomically — there are no partial amendment states visible. When you poll order state, you will see either the pre-amendment state or the fully-applied amendment, never an intermediate state.

Request collapsing: If multiple amendment requests are submitted in rapid succession, only the latest request is processed (see Section 1.2). If the order state shows no validation_error after polling, the most recently submitted amendment was successfully applied. The order's current goal_quantity, limit, and expire_at fields reflect the applied parameters.


4.3 Cancel Order

POST /orders/{order_id}/cancel

Submits a cancellation request for an order.

Note

POST is used (rather than DELETE) to allow a request body for audit purposes and because cancellation is a state transition rather than resource deletion.

Request Body (Optional)

JSON
{ "reason": "Client requested" }

Response (Success - 202 Accepted)

JSON
{ "request_id": "req_ghi789", "order_id": "ord_7f3a9b2c", "status": "sent_to_ems", "message": "Cancel request sent to execution system. Poll order state for updates.", "submitted_at": "2025-01-15T10:35:00.456Z", "links": { "order": "/orders/ord_7f3a9b2c", "request": "/requests/req_ghi789" } }

4.4 Cancel All Orders

POST /orders/cancel-all

Submits a cancellation request for all working (non-completed) orders. Useful for emergency situations or end-of-day cleanup.

Request Body (Optional)

JSON
{ "reason": "End of day cleanup", "filter": { "instrument_type": "irs_imm", "currency": "USD", "allocation": "FUND_ABC" } }
Field Required Description
reason No Audit trail reason for bulk cancellation
filter.instrument_type No Only cancel orders for this instrument type
filter.currency No Only cancel orders for this currency
filter.allocation No Only cancel orders for this allocation target

If no filter is provided, all non-completed orders are cancelled.

Response (Success - 202 Accepted)

JSON
{ "request_id": "req_bulk_001", "status": "sent_to_ems", "message": "Bulk cancel request sent to execution system. Poll order states for updates.", "submitted_at": "2025-01-15T16:00:00.123Z", "orders_targeted": 12, "links": { "orders": "/orders?completed=false" } }
Note

`orders_targeted` indicates how many orders matched the filter at the time of the request. Some may complete before the cancellation is processed.


4.5 Get Order State

GET /orders/{order_id}

Retrieves current state of an order from the EMS. This is the source of truth for order status.

Response (Success - 200 OK)

JSON
{ "order_id": "ord_7f3a9b2c", "client_order_id": "my-order-12345", "status": "PARTIALLY_FILLED", "instrument": { "type": "irs_imm", "currency": "USD", "imm_date": "H25", "tenor": "10Y", "description": "Mar 25 10Y USD vs SOFR" }, "side": "pay", "goal_quantity": { "notional": 50000000 }, "filled_quantity": { "notional": 20000000 }, "limit": { "rate": 4.25 }, "average_price": 4.2475, "price_protection": "market", "time_in_force": "GTT", "expire_at": "2025-01-15T16:00:00Z", "allocation": "FUND_ABC", "completed": false, "validation_error": null, "created_at": "2025-01-15T10:30:00.123Z", "updated_at": "2025-01-15T10:32:15.789Z", "fills": [ { "fill_id": "fill_abc123", "quantity": { "notional": 20000000 }, "price": 4.2475, "filled_at": "2025-01-15T10:32:15.789Z", "venue": "D2D" } ], "links": { "self": "/orders/ord_7f3a9b2c", "cancel": "/orders/ord_7f3a9b2c/cancel", "requests": "/requests?order_id=ord_7f3a9b2c" } }

Key Fields

Field Description
status Human-readable order status: NEW, WORKING, PARTIALLY_FILLED, FILLED, CANCELLED, EXPIRED, REJECTED. See state definitions below.
completed true means order has no further market exposure (filled, cancelled, expired, or rejected). false means order may still trade.
validation_error If not null, the EMS rejected something in the latest request for this order. Client should correct and resubmit. Cleared when a subsequent request for this order is processed successfully.
goal_quantity Current target quantity (reflects latest processed amendment)
filled_quantity How much has traded

Order State Machine

The status field reflects the current state of the order:

                                    ┌─────────────────────────────────────┐
                                    │                                     │
                                    ▼                                     │
┌─────────────┐    EMS accepts    ┌─────────────┐    partial fill    ┌─────────────┐
│             │ ───────────────►  │             │ ─────────────────► │  PARTIALLY  │
│     NEW     │                   │   WORKING   │                    │   FILLED    │──┐
│             │                   │             │ ◄───────────────── │             │  │
└─────────────┘                   └─────────────┘    amend processed └─────────────┘  │
      │                                 │                                   │         │
      │ validation_error                │                                   │         │
      ▼                                 │ cancel                            │         │
┌─────────────┐                         ▼                                   │         │
│  REJECTED   │                   ┌─────────────┐                           │         │
│             │                   │  CANCELLED  │                           │         │
└─────────────┘                   └─────────────┘                           │         │
                                        │ expire                            │ final   │
                                        ▼                                   │ fill    │
                                  ┌─────────────┐                           │         │
                                  │   EXPIRED   │                           │         │
                                  └─────────────┘                           │         │
                                                                            │         │
                                  ┌─────────────┐                           │         │
                                  │   FILLED    │ ◄─────────────────────────┘         │
                                  │             │ ◄───────────────────────────────────┘
                                  └─────────────┘

Status Values:

Status completed Description
NEW false Order created, awaiting EMS processing
WORKING false Order active in market, no fills yet
PARTIALLY_FILLED false Order has some fills, still working remaining quantity
FILLED true Order fully filled
CANCELLED true Order cancelled by client or system
EXPIRED true Order expired (GTT/EOD time reached)
REJECTED true Order rejected by EMS (check validation_error)

Transition Triggers:

Response with Validation Error

JSON
{ "order_id": "ord_7f3a9b2c", "client_order_id": "my-order-12345", "status": "PARTIALLY_FILLED", "goal_quantity": { "notional": 50000000 }, "filled_quantity": { "notional": 30000000 }, "completed": false, "validation_error": { "code": "INVALID_QUANTITY_INCREMENT", "message": "Quantity must be in increments of 1MM", "field": "goal_quantity" }, "updated_at": "2025-01-15T10:32:15.789Z", "links": { "self": "/orders/ord_7f3a9b2c", "requests": "/requests?order_id=ord_7f3a9b2c" } }
Note

The `validation_error` applies to the most recent amendment request. The order continues working at its previous parameters. When the client submits a corrected amendment and it processes successfully, the `validation_error` is cleared.


4.6 List Orders

GET /orders

Lists orders for the current session with filtering and pagination.

Query Parameters

Parameter Description
completed Filter: true (terminal), false (still working)
instrument_type Filter by type: irs_imm, irs_spot, etc.
currency Filter by currency
client_order_id Find by client reference
limit Max results (default 50, max 200)
cursor Pagination cursor

Example

GET /orders?completed=false&currency=USD&limit=10

Response (Success - 200 OK)

JSON
{ "orders": [ { /* order object */ }, { /* order object */ } ], "pagination": { "total": 45, "limit": 10, "has_more": true, "next_cursor": "eyJpZCI6Im9yZF8xMjM0In0=" } }

5. Request Log Endpoints

The request log tracks all requests submitted by the client. Useful for: - Confirming a request reached the gateway - Debugging after disconnection/reconnection - Audit trail

5.1 Get Request

GET /requests/{request_id}

Response (Success - 200 OK)

JSON
{ "request_id": "req_abc123", "type": "create_order", "order_id": "ord_7f3a9b2c", "client_order_id": "my-order-12345", "status": "sent_to_ems", "submitted_at": "2025-01-15T10:30:00.123Z", "payload": { "instrument": { "type": "irs_imm", "currency": "USD", "imm_date": "H25", "tenor": "10Y" }, "side": "pay", "quantity": { "notional": 50000000 }, "allocation": "FUND_ABC" }, "links": { "order": "/orders/ord_7f3a9b2c" } }

Request Statuses

Status Description
rejected_by_gateway Gateway rejected request (auth, permissions, format)
sent_to_ems Request forwarded to EMS
Note

There is no `processed` or `acknowledged` status. Once `sent_to_ems`, the request outcome is observed via order state, not the request log.

5.2 List Requests

GET /requests

Query Parameters

Parameter Description
order_id Filter by order
type Filter: create_order, amend_order, cancel_order
status Filter: rejected_by_gateway, sent_to_ems
limit Max results (default 50, max 200)
cursor Pagination cursor

Response (Success - 200 OK)

JSON
{ "requests": [ { "request_id": "req_abc123", "type": "create_order", "order_id": "ord_7f3a9b2c", "status": "sent_to_ems", "submitted_at": "2025-01-15T10:30:00.123Z", "links": { "order": "/orders/ord_7f3a9b2c" } }, { "request_id": "req_def456", "type": "amend_order", "order_id": "ord_7f3a9b2c", "status": "sent_to_ems", "submitted_at": "2025-01-15T10:33:00.123Z", "links": { "order": "/orders/ord_7f3a9b2c" } } ], "pagination": { "total": 2, "limit": 50, "has_more": false } }

6. Reference Data Endpoints

6.1 Get Instruments

GET /instruments

Lists available tradeable instruments.

Query Parameters

Parameter Description
type Filter by type: irs_imm, irs_spot, govt_bond, etc.
currency Filter by currency

Response (Success - 200 OK)

JSON
{ "instruments": [ { "type": "irs_imm", "currency": "USD", "imm_date": "H25", "tenor": "10Y", "description": "Mar 25 10Y USD vs SOFR", "index": "SOFR", "min_quantity": { "notional": 1000000 }, "quantity_increment": { "notional": 1000000 }, "price_increment": 0.0025, "tradeable": true } ] }

6.2 Get Allocations

GET /allocations

Lists allocation targets available to the client.

Response (Success - 200 OK)

JSON
{ "allocations": [ { "id": "FUND_ABC", "name": "ABC Fixed Income Fund", "currency": "USD", "active": true }, { "id": "FUND_XYZ", "name": "XYZ Multi-Asset", "currency": "EUR", "active": true } ] }
Note

Only allocations returned by this endpoint can be used in orders. Attempting to use an unlisted allocation will result in a permission error.


7. Health & Status Endpoints

GET /health

Simple health check for load balancers and monitoring. Returns 200 if the gateway is operational.

Response (Success - 200 OK)

JSON
{ "status": "healthy", "timestamp": "2025-01-15T10:30:00.123Z" }

Response (Degraded - 503 Service Unavailable)

JSON
{ "status": "unhealthy", "timestamp": "2025-01-15T10:30:00.123Z", "reason": "EMS connection unavailable" }
Note

This endpoint does not require authentication and is intended for infrastructure monitoring.

GET /status

Detailed system status including EMS connectivity and current session info. Requires authentication.

Response (Success - 200 OK)

JSON
{ "gateway": { "status": "healthy", "version": "1.2.3", "instance_id": "gw-us-east-1a-001" }, "ems": { "status": "connected", "latency_ms": 12 }, "session": { "current": "2025-01-15", "started_at": "2025-01-15T00:00:00.000Z", "ends_at": "2025-01-15T23:59:59.999Z" }, "client": { "client_id": "client_abc123", "open_orders": 5, "requests_today": 1247 }, "timestamp": "2025-01-15T10:30:00.123Z" }

8. Client Integration Pattern

1. Submit request (create/amend/cancel)
2. Receive 202 Accepted with request_id and order_id
3. Poll GET /orders/{order_id} at appropriate interval:
   - Active trading: every 1-2 seconds
   - Passive monitoring: every 5-10 seconds
4. Check order state:
   - If validation_error is set → fix and resubmit
   - If completed is true → order is done, record final state
   - Otherwise → order still working, continue polling

8.2 Reconnection/Recovery

After disconnection or client restart:

1. Call GET /requests to see all requests sent this session
2. Call GET /orders?completed=false to see all working orders
3. For each working order, verify current state matches expectations
4. Resume normal polling

8.3 Example: Create and Monitor Order

PYTHON
import requests import time import uuid API_BASE = "https://api.ediphymarkets.com/ems/v1" API_KEY = "ak_live_7f3a9b2c4d5e6f..." # Your API key headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } # 1. Create order order_payload = { "instrument": {"type": "irs_imm", "currency": "USD", "imm_date": "H25", "tenor": "10Y"}, "side": "pay", "quantity": {"notional": 50000000}, # 50MM "allocation": "FUND_ABC", "client_order_id": "strategy-alpha-001", "time_in_force": "EOD", } response = requests.post( f"{API_BASE}/orders", json=order_payload, headers={**headers, "Idempotency-Key": str(uuid.uuid4())}, ) response.raise_for_status() order_id = response.json()["order_id"] # 2. Poll until complete while True: order = requests.get(f"{API_BASE}/orders/{order_id}", headers=headers).json() if order["status"] == "REJECTED": raise Exception(f"Order rejected: {order['validation_error']['message']}") if order["completed"]: filled = order["filled_quantity"]["notional"] / 1_000_000 print(f"Order complete: filled {filled}MM at avg {order['average_price']}") break # Still working filled = order["filled_quantity"]["notional"] / 1_000_000 goal = order["goal_quantity"]["notional"] / 1_000_000 print(f"Status: {order['status']} — filled {filled}MM of {goal}MM") time.sleep(2)

9. WebSocket Streaming

9.1 Overview

For clients requiring real-time updates, the gateway provides a WebSocket stream that delivers order updates and fills as they occur, eliminating the need for polling.

Production:  wss://api.ediphymarkets.com/ems/v1/stream
Sandbox:     wss://api.sandbox.ediphymarkets.com/ems/v1/stream

9.2 Connection & Authentication

Bearer Token Authentication

Connect to the WebSocket endpoint and send an authentication message within 5 seconds:

wss://api.ediphymarkets.com/ems/v1/stream

Authentication message:

JSON
{"type": "auth", "api_key": "<api_key>"}

mTLS Authentication

Connect to the same WebSocket endpoint with your client certificate:

wss://api.ediphymarkets.com/ems/v1/stream

No authentication message required — if a valid client certificate is presented during the TLS handshake, identity is established at the transport layer. The server sends connected immediately after the connection is established.

Connection lifecycle:

  1. Client connects to WebSocket endpoint (with certificate for mTLS)
  2. For Bearer token: Client sends authentication message within 5 seconds
  3. Server validates credentials and sends connected message (or closes connection on failure)
  4. Client receives updates for all orders in current session
  5. Both parties send heartbeats to maintain connection (see Section 9.3)

9.3 Heartbeat Protocol

Both client and server must send heartbeats to maintain the connection. This allows prompt detection of dead connections without relying on TCP timeouts.

Direction Interval Timeout Action on Timeout
Server → Client Every 30 seconds 90 seconds Client should disconnect and reconnect
Client → Server Every 30 seconds 90 seconds Server disconnects client

Client heartbeat:

JSON
{"type": "heartbeat"}

Server heartbeat:

JSON
{"type": "heartbeat", "timestamp": "2025-01-15T10:30:00.123Z"}
Important

If the server does not receive a client heartbeat within 90 seconds, the connection will be terminated. Conversely, if the client does not receive a server heartbeat within 90 seconds, it should assume the connection is dead, disconnect, and initiate reconnection (see Section 9.6). Clients should send heartbeats at least every 30 seconds to maintain the connection.

9.4 Server Messages

Connected

Sent after successful authentication:

JSON
{ "type": "connected", "session": "2025-01-15", "timestamp": "2025-01-15T10:30:00.123Z" }

Order Update

Sent when any order state changes (new order, amendment processed, fill, cancellation, expiry):

JSON
{ "type": "order_update", "order_id": "ord_7f3a9b2c", "timestamp": "2025-01-15T10:32:15.789Z", "data": { "order_id": "ord_7f3a9b2c", "client_order_id": "my-order-12345", "status": "PARTIALLY_FILLED", "instrument": { "type": "irs_imm", "currency": "USD", "imm_date": "H25", "tenor": "10Y" }, "side": "pay", "goal_quantity": { "notional": 50000000 }, "filled_quantity": { "notional": 20000000 }, "limit": { "rate": 4.25 }, "completed": false, "validation_error": null, "updated_at": "2025-01-15T10:32:15.789Z" } }

Fill

Sent when a fill occurs (in addition to the order_update):

JSON
{ "type": "fill", "order_id": "ord_7f3a9b2c", "timestamp": "2025-01-15T10:32:15.789Z", "fill": { "fill_id": "fill_abc123", "quantity": { "notional": 20000000 }, "price": 4.2475, "filled_at": "2025-01-15T10:32:15.789Z", "venue": "D2D" } }
Note

Fill messages are provided for convenience but can be safely ignored. The `order_update` message contains the complete order state including cumulative `filled_quantity`. Clients who only need to track order state can process `order_update` messages exclusively.

Error

Sent when an error occurs on the stream:

JSON
{ "type": "error", "code": "INVALID_MESSAGE", "message": "Unrecognized message type", "timestamp": "2025-01-15T10:30:00.123Z" }

Disconnecting

Sent before server-initiated disconnect:

JSON
{ "type": "disconnecting", "reason": "Session ended", "timestamp": "2025-01-15T23:59:59.000Z" }

9.5 Client Messages

Type Description
auth Initial authentication
heartbeat Keep-alive signal
reauthenticate Re-authenticate with new credentials (e.g., after key rotation)

Reauthentication (Bearer Token Only)

If API credentials are rotated while a WebSocket connection is active, clients using Bearer token authentication can reauthenticate without disconnecting:

JSON
{"type": "reauthenticate", "api_key": "<new_api_key>"}

Server response on success:

JSON
{"type": "reauthenticated", "timestamp": "2025-01-15T10:30:00.123Z"}

Server response on failure:

JSON
{"type": "error", "code": "AUTHENTICATION_FAILED", "message": "Invalid credentials", "timestamp": "..."}
Note

Failed reauthentication does not disconnect the session if the original credentials are still valid. The connection continues using the original authentication.

mTLS Note

Reauthentication is not supported for mTLS connections. To rotate certificates, establish a new connection with the new certificate.

Unknown message types are ignored.

9.6 Reconnection

If the WebSocket connection drops:

  1. Wait 1 second, then attempt reconnect
  2. On failure, use exponential backoff (2s, 4s, 8s, max 30s)
  3. After reconnecting, call GET /orders?completed=false to sync state
  4. Resume normal operation
Note

The WebSocket stream does not replay missed messages. After reconnection, clients must use the REST API to query current order state.

9.7 Example Session

Client: connects to wss://api.ediphymarkets.com/ems/v1/stream
Client: {"type": "auth", "api_key": "ak_live_7f3a9b2c..."}
Server: {"type": "connected", "session": "2025-01-15", "timestamp": "..."}

Client: {"type": "heartbeat"}
Server: {"type": "heartbeat", "timestamp": "..."}

[Client creates order via REST API]
Server: {"type": "order_update", "order_id": "ord_7f3a9b2c", "data": {...}}

[Fill occurs]
Server: {"type": "fill", "order_id": "ord_7f3a9b2c", "fill": {...}}
Server: {"type": "order_update", "order_id": "ord_7f3a9b2c", "data": {...}}

Client: {"type": "heartbeat"}
Server: {"type": "heartbeat", "timestamp": "..."}

[Session ending]
Server: {"type": "disconnecting", "reason": "Session ended", "timestamp": "..."}
Server: closes connection

10. Rate Limiting

10.1 Default Limits

Rate limits are applied per connection (i.e., per authenticated API key per TCP connection).

Endpoint Rate Limit
POST /orders 10/second, 100/minute
PATCH /orders/{id} 10/second
POST /orders/{id}/cancel 10/second
GET /orders/{id} 100/second
GET /orders 20/second
GET /requests 20/second
GET /instruments 10/second
GET /allocations 10/second
Note

Rate limit breaches only affect new requests. Existing working orders are not impacted — they continue to trade normally regardless of the client's rate limit status.

Need higher limits? Contact support@ediphymarkets.com to discuss increased rate limits for algorithmic trading or high-volume use cases.

10.2 Response Headers

X-Ediphy-RateLimit-Limit: 100
X-Ediphy-RateLimit-Remaining: 95
X-Ediphy-RateLimit-Reset: 2025-01-15T10:31:00Z

10.3 Rate Limit Exceeded (429)

JSON
{ "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests", "retry_after": 5 } }

11. Security Considerations

11.1 Transport Security

11.2 Authentication Security

11.3 IP Allowlisting

11.4 Audit Trail

All API requests are logged for compliance, dispute resolution, and regulatory reporting. Clients can request audit reports for any time period by contacting support@ediphymarkets.com.


12. Versioning & Compatibility

12.1 Version Policy

The API version is included in the URL path (e.g., /ems/v1). Ediphy maintains backwards compatibility within a major version.

Supported versions: Ediphy supports the current version and the immediately previous version. Clients are encouraged to migrate to the latest version when available.

Non-breaking changes (may be introduced without version bump): - Adding new optional fields to responses - Adding new optional parameters to requests - Adding new endpoints - Adding new enum values (clients should handle unknown values gracefully)

Breaking changes (require new version): - Removing or renaming fields - Changing field types - Changing required/optional status of fields - Removing endpoints - Changing authentication mechanisms

12.2 Deprecation Process

When a breaking change is necessary:

  1. New version is released (e.g., /ems/v2) with changes documented
  2. Clients are notified via email at least 90 days before old version sunset
  3. Previous version continues to function during migration period
  4. After sunset date, old version returns 410 Gone

Migration support: Ediphy will provide migration guides and support for version transitions. Contact support@ediphymarkets.com for assistance.


13. Sandbox Environment

The sandbox environment allows clients to: - Test integration without real orders - Use test instruments with simulated fills - Validate authentication and permissions

Sandbox Behaviors:

Test Endpoints (Sandbox Only):

POST /test/orders/{order_id}/fill    # Simulate fill
POST /test/orders/{order_id}/expire  # Simulate expiry

14. Endpoint Summary

Method Endpoint Description
POST /orders Create new order (202 Accepted)
GET /orders List orders (current session)
GET /orders/{id} Get order state (source of truth)
PATCH /orders/{id} Amend order (202 Accepted)
POST /orders/{id}/cancel Cancel single order (202 Accepted)
POST /orders/cancel-all Cancel all working orders (202 Accepted)
GET /requests List requests sent this session
GET /requests/{id} Get request details
GET /instruments List tradeable instruments
GET /allocations List client's allocation targets
GET /health Gateway health check (no auth required)
GET /status Detailed system status (auth required)
WSS /stream WebSocket stream for real-time updates