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?
- Simple Integration — Clean REST interface with JSON payloads; no proprietary protocols or complex dependencies
- Real-Time Visibility — WebSocket streaming for instant order state updates and execution notifications
- Built-In Safety — Automatic heartbeat monitoring with configurable auto-cancel protects against runaway orders
- Full Audit Trail — Every request is logged and queryable, supporting your compliance and reconciliation workflows
- Flexible Order Management — Support for create, amend, cancel, and bulk operations with idempotency guarantees
- Sandbox Environment — Test your integration against a full-featured simulation before going live
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:
- REST API clients must call any authenticated endpoint at least once every 60 seconds
- WebSocket clients must send heartbeat messages at least every 30 seconds (see Section 9.3)
Auto-Cancel on Heartbeat Failure:
- If no authenticated activity is received for 90 seconds, the gateway assumes the client has lost control
- By default, all non-GTC orders (GTT, EOD) are automatically cancelled
- GTC (good-till-cancel) orders are preserved across heartbeat failures
- Clients are notified via WebSocket (if connected) before auto-cancel executes
- This behavior can be configured per-client (contact support@ediphymarkets.com)
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:
- Client submits a request (create, amend, cancel) describing the desired state
- The API persists the request and queues it for processing
- The EMS processes the latest request for each order when ready
- Client polls order state to observe changes
- Order state includes
completedflag (true = no further market exposure) andvalidation_errorfield
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:
- Start: 00:00:00.000 UTC
- End: 23:59:59.999 UTC
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>
- Simple integration — Works with any standard HTTP client
- No cryptographic operations — No signing or key management required client-side
- Suitable for most use cases — Protected by TLS encryption and idempotency keys
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:
- Client generates an X.509 certificate and private key
- Client registers the certificate (or CA) via the Ediphy client portal
- TLS handshake authenticates both client and server
- No
Authorizationheader required — identity established at transport layer
Benefits:
- Stronger authentication — Cryptographic proof of client identity at connection time
- No shared secrets — Private key never leaves client infrastructure
- Connection-level security — Authentication happens before any application data is exchanged
- Standard tooling — Works with standard TLS libraries and infrastructure
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:
- If a valid client certificate is presented → mTLS authentication is used; no
Authorizationheader required - If no certificate is presented → Bearer token authentication is required via the
Authorizationheader
This allows clients to choose their authentication method on a per-connection basis without changing endpoints.
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:
- Generate — Create new API credentials with a single click; the secret is shown once and must be saved securely
- Revoke — Immediately invalidate a compromised key
- Clone — Duplicate permissions from an existing key to a new key (useful when provisioning new systems)
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.
- Format: Any unique string up to 128 characters. Allowed characters: alphanumeric, hyphens, and underscores (
a-zA-Z0-9_-). UUIDs are recommended (e.g.,550e8400-e29b-41d4-a716-446655440000) - Scope: Session (resets at 00:00 UTC)
- Behavior: If the gateway receives a request with the same
Idempotency-Keywithin the same session, it returns the cached response from the original request - Uniqueness: Generate a new UUID for each distinct request; reuse the same UUID only when retrying a failed request
`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:
AUTHENTICATION_FAILED- Invalid credentialsPERMISSION_DENIED- Not authorized for action/productVALIDATION_ERROR- Invalid request parameters (gateway-level)DUPLICATE_CLIENT_ORDER_ID-client_order_idalready in use by an active orderLIMIT_EXCEEDED- Rate/size limit exceededINSTRUMENT_NOT_FOUND- Unknown instrumentORDER_NOT_FOUND- Order doesn't existNOTIONAL_CAP_EXCEEDED- DV01 conversion would exceed specified notional capRATE_LIMIT_EXCEEDED- Too many requests (see rate limiting)INTERNAL_ERROR- Server-side issue
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
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:
- Generate a fresh UUID for each new order request
- If a request times out, retry with the same
Idempotency-Key - The gateway returns the cached response if the key was already processed
- Keys are session-scoped and reset at 00:00 UTC
client_order_id Field (Business Logic)
The client_order_id provides a meaningful business identifier for the order:
- Scope: Must be unique among all active (non-completed) orders for the client
- Collision handling: If you submit an order with a
client_order_idthat matches an existing active order, the request is rejected withDUPLICATE_CLIENT_ORDER_IDerror - Reuse after completion: Once an order reaches a terminal state (
FILLED,CANCELLED,EXPIRED,REJECTED), itsclient_order_idcan be reused for a new order
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 } }
dv01_factor— The factor used to convert DV01 to notional (notional per DV01 unit)notional— The actual notional being traded, calculated asdv01 × dv01_factor- DV01 factors are also available in the Ediphy GUI under instrument details and via
GET /instruments
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" } }
`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
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 |
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" } }
**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
Submits a cancellation request for an order.
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
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" } }
`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
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:
- NEW → WORKING: EMS accepts and activates the order
- NEW → REJECTED: EMS rejects order (invalid instrument, limit violation, etc.)
- WORKING → PARTIALLY_FILLED: First fill received
- PARTIALLY_FILLED → PARTIALLY_FILLED: Additional fills, or amendment processed
- WORKING/PARTIALLY_FILLED → FILLED: Goal quantity fully filled
- WORKING/PARTIALLY_FILLED → CANCELLED: Cancel request processed
- WORKING/PARTIALLY_FILLED → EXPIRED: Expiry time reached
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" } }
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
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¤cy=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
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 |
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
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
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
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 } ] }
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" }
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
8.1 Recommended Polling Strategy
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
PYTHONimport 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:
- Client connects to WebSocket endpoint (with certificate for mTLS)
- For Bearer token: Client sends authentication message within 5 seconds
- Server validates credentials and sends
connectedmessage (or closes connection on failure) - Client receives updates for all orders in current session
- 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"}
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" } }
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": "..."}
Failed reauthentication does not disconnect the session if the original credentials are still valid. The connection continues using the original authentication.
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:
- Wait 1 second, then attempt reconnect
- On failure, use exponential backoff (2s, 4s, 8s, max 30s)
- After reconnecting, call
GET /orders?completed=falseto sync state - Resume normal operation
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 |
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
- TLS 1.3 required for all connections
- mTLS available for clients requiring mutual authentication (see Section 2.1)
- Certificate pinning recommended for high-security clients
- VPN connections available for high-volume clients (contact support@ediphymarkets.com)
11.2 Authentication Security
- Bearer tokens — Protected by TLS encryption; suitable for most integrations
- mTLS — Cryptographic client authentication at transport layer; recommended for high-security environments
- Idempotency keys (
client_order_id) prevent duplicate order creation
11.3 IP Allowlisting
- Clients can optionally register allowed IP ranges
- Requests from unknown IPs rejected
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:
- New version is released (e.g.,
/ems/v2) with changes documented - Clients are notified via email at least 90 days before old version sunset
- Previous version continues to function during migration period
- 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:
- Orders accepted but routed to simulation
- Fills generated on schedule or via test endpoint
- No real market interaction
- Same permissions model as production
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 |