{
	"openapi": "3.0.3",
	"info": {
		"title": "Purple Engine API",
		"version": "1.0.0",
		"description": "Unified fintech API for Purple brands (Axiory, Purple Trading, Fintokei) and third-party applications.\n\nEach business unit is served by its own gateway host; on production, Axiory's is purple-engine.axiory.com. The host selects the gateway and the services behind it; the brand in the access token does not.\n\nThe gateway refuses a call before any service sees it with 401 or 403 and an `error.code`, among them `TOKEN_EXPIRED` (renew or sign in again), `SCOPE_INSUFFICIENT` (the app lacks the permission, or its sign-in predates it), `ROUTE_NOT_CONFIGURED` (the operation is turned off for the app), `PARAM_NOT_PERMITTED` (an argument outside the app's filter), `APP_NOT_OWNER` (a personal app signed in with another brand account than its owner) and `APP_INACTIVE` (the app was revoked)."
	},
	"servers": [
		{
			"url": "https://purple-engine-axi-plt-1490.murban.personal.purple-lab.dev",
			"description": "Purple Engine API"
		}
	],
	"paths": {
		"/health": {
			"get": {
				"summary": "Health check",
				"operationId": "getHealth",
				"tags": [
					"system"
				],
				"security": [],
				"responses": {
					"200": {
						"description": "Service is healthy",
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"status": {
											"type": "string",
											"enum": [
												"ok"
											]
										}
									}
								}
							}
						}
					}
				}
			}
		},
		"/v1/app-info": {
			"get": {
				"summary": "App metadata for OIDC consent screen",
				"description": "Public endpoint (no auth, no API key). Returns the third-party app's name, description, and capabilities so the brand's consent screen can render the approval flow. Anti-enumeration: malformed, unknown, and fully-inactive `app_id` values all return the same 200 shape with `name: ''` and `capabilities: []`. Pair with the WAF rule `AppInfoRateLimit` (100 req/min/IP).",
				"operationId": "getAppInfo",
				"tags": [
					"public"
				],
				"security": [],
				"parameters": [
					{
						"name": "app_id",
						"in": "query",
						"required": true,
						"description": "Application identifier from the App Registry.",
						"schema": {
							"type": "string",
							"pattern": "^[a-zA-Z0-9._-]+$"
						}
					}
				],
				"responses": {
					"200": {
						"description": "App metadata. `capabilities.length === 0` means unknown / invalid / deactivated — consent screen MUST NOT render the approve button.",
						"headers": {
							"Cache-Control": {
								"schema": {
									"type": "string"
								},
								"description": "public, max-age=300"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/AppInfo"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal error (DynamoDB / engine-internal failure). Real outages are NOT suppressed into the empty-200 shape.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/requests/{requestId}": {
			"get": {
				"summary": "Read the status of an asynchronous request",
				"description": "Returns the status of an asynchronous request that the calling app created for the calling user, on either environment. Needs no scope: a request enters an app's namespace only through a scoped create call, and a request outside the caller's namespace answers the same 404 as an unknown id. Every `202` from an asynchronous endpoint names this URL in its `Location` header.",
				"operationId": "getRequestStatus",
				"tags": [
					"requests"
				],
				"parameters": [
					{
						"name": "requestId",
						"in": "path",
						"required": true,
						"description": "The `requestId` an asynchronous endpoint returned in its `202` body.",
						"schema": {
							"type": "string"
						}
					}
				],
				"responses": {
					"200": {
						"description": "The request's current status.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/RequestStatusResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "Unknown request, or one the caller did not create.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/credentials/ctrader": {
			"get": {
				"operationId": "readDemoFixCredentials",
				"summary": "Check whether one of the caller's demo cTrader accounts has an investor password",
				"description": "Pure, idempotent check of whether an investor password is stored for the account the accountId names — safe to call on every connect. No broker call, no KMS call, no DDB write. Returns `{ status: \"stored\", provisionedAt }` when a password exists, or `{ status: \"none\" }` when none does (the caller then provisions). The password itself is never returned. The account is resolved and its scope checked before the store is read. Supported for cTrader accounts only (MT4/MT5/YourBourse → 422).",
				"x-purple-target": {
					"type": "lambda",
					"resource": "FixCredentialsReadLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Whether an investor password is stored",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/FixCredentialsReadResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover its credentials",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not a cTrader account (MT4/MT5/YourBourse not supported), or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Account data or account identifiers unavailable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			},
			"post": {
				"operationId": "provisionDemoFixCredentials",
				"summary": "Provision (create-if-absent) an investor password for one of the caller's demo cTrader accounts",
				"description": "Create-if-absent provisioning of the investor password for the account the accountId names. When no password exists, uses the caller-supplied `password` if present (else mints one), sets it at the broker via Fintech (cTrader changePassword), encrypts it with KMS, persists the ciphertext, and returns `{ status: \"provisioned\", provisionedAt }` — never the password. A supplied password must satisfy the investor-password policy (8–16 chars, mixed case + digit + symbol, no whitespace) or is rejected with 400. When a password already exists, returns 409 with no broker write (a supplied password never overwrites an existing one). There is no force/override or rotation path. The account is resolved and its scope checked before anything is issued, and re-checked against a fresh trader-row read on every acting step. Supported for cTrader accounts only (MT4/MT5/YourBourse → 422). A concurrent provision for the same account returns 503.",
				"x-purple-target": {
					"type": "sync-sfn",
					"resource": "FixCredentialsProvisionSfn"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/FixCredentialsStageProvisionRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Investor password provisioned (not returned)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/FixCredentialsProvisionResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover its credentials",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"409": {
						"description": "A password is already provisioned for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not a cTrader account (MT4/MT5/YourBourse not supported), or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Concurrent provisioning in progress or recent failure (retry after the lease window), account data or account identifiers unavailable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/credentials/ctrader": {
			"get": {
				"operationId": "readLiveFixCredentials",
				"summary": "Check whether one of the caller's live cTrader accounts has an investor password",
				"description": "Pure, idempotent check of whether an investor password is stored for the account the accountId names — safe to call on every connect. No broker call, no KMS call, no DDB write. Returns `{ status: \"stored\", provisionedAt }` when a password exists, or `{ status: \"none\" }` when none does (the caller then provisions). The password itself is never returned. The account is resolved and its scope checked before the store is read. Supported for cTrader accounts only (MT4/MT5/YourBourse → 422).",
				"x-purple-target": {
					"type": "lambda",
					"resource": "FixCredentialsReadLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Whether an investor password is stored",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/FixCredentialsReadResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover its credentials",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not a cTrader account (MT4/MT5/YourBourse not supported), or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Account data or account identifiers unavailable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			},
			"post": {
				"operationId": "provisionLiveFixCredentials",
				"summary": "Provision (create-if-absent) an investor password for one of the caller's live cTrader accounts",
				"description": "Create-if-absent provisioning of the investor password for the account the accountId names. When no password exists, uses the caller-supplied `password` if present (else mints one), sets it at the broker via Fintech (cTrader changePassword), encrypts it with KMS, persists the ciphertext, and returns `{ status: \"provisioned\", provisionedAt }` — never the password. A supplied password must satisfy the investor-password policy (8–16 chars, mixed case + digit + symbol, no whitespace) or is rejected with 400. When a password already exists, returns 409 with no broker write (a supplied password never overwrites an existing one). There is no force/override or rotation path. The account is resolved and its scope checked before anything is issued, and re-checked against a fresh trader-row read on every acting step. Supported for cTrader accounts only (MT4/MT5/YourBourse → 422). A concurrent provision for the same account returns 503.",
				"x-purple-target": {
					"type": "sync-sfn",
					"resource": "FixCredentialsProvisionSfn"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/FixCredentialsStageProvisionRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Investor password provisioned (not returned)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/FixCredentialsProvisionResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover its credentials",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"409": {
						"description": "A password is already provisioned for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not a cTrader account (MT4/MT5/YourBourse not supported), or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Concurrent provisioning in progress or recent failure (retry after the lease window), account data or account identifiers unavailable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/accounts": {
			"get": {
				"operationId": "getDemoAccounts",
				"summary": "List the caller's demo trading accounts",
				"description": "Returns the authenticated caller's demo trading accounts only, each with the opaque accountId the other /v1/trading-account/demo/* routes address it by. The caller's YourBourse accounts are included only where this deployment serves them on this stage. Identity is resolved via the engine-supplied brandClientId → CRM crosswalk; no caller-supplied input is read. Empty result (no CRM row, or no accounts on this stage) returns 200 with `accounts: []`. When the YourBourse store cannot be read but the report server answered, the report-server accounts are returned and the failure is logged.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetAccountsByTraderEmailLambda"
				},
				"responses": {
					"200": {
						"description": "Accounts for the caller on this stage (possibly empty)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/GetStageAccountsResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error (malformed identity or envelope fields)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "The forwarded scopes do not hold purple-engine/trading-account.demo.accounts.read",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "The request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "This stage's report server or account identifiers unavailable, or the engine relied on a default this service does not apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			},
			"post": {
				"operationId": "createDemoTradingAccount",
				"summary": "Open a demo Axiory trading account on cTrader or YourBourse",
				"description": "Async capability that opens a demo trading account for the end-user resolved from the verified envelope (never a request field). `Idempotency-Key` is a required HTTP header (consumed by the engine async proxy, never seen by this service). Returns 202 immediately with a `Location` header naming the status path of the request; poll that path, or receive a Standard-Webhooks-v1 signed callback at `callbackUrl` when the app takes callbacks. The status and callback payloads are those of the old account-provisioning routes.",
				"x-purple-target": {
					"type": "async-sfn",
					"resource": "AccountProvisioningDemoSfn"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CreateDemoAccountRequestBody"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Request accepted; account opening proceeds asynchronously. The `Location` header carries the status path of the request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/AccountProvisioningAcceptedResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/accounts": {
			"get": {
				"operationId": "getLiveAccounts",
				"summary": "List the caller's live trading accounts",
				"description": "Returns the authenticated caller's live trading accounts only, each with the opaque accountId the other /v1/trading-account/live/* routes address it by. The caller's YourBourse accounts are included only where this deployment serves them on this stage. Identity is resolved via the engine-supplied brandClientId → CRM crosswalk; no caller-supplied input is read. Empty result (no CRM row, or no accounts on this stage) returns 200 with `accounts: []`. When the YourBourse store cannot be read but the report server answered, the report-server accounts are returned and the failure is logged.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetAccountsByTraderEmailLambda"
				},
				"responses": {
					"200": {
						"description": "Accounts for the caller on this stage (possibly empty)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/GetStageAccountsResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error (malformed identity or envelope fields)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "The forwarded scopes do not hold purple-engine/trading-account.live.accounts.read",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "The request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "This stage's report server or account identifiers unavailable, or the engine relied on a default this service does not apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			},
			"post": {
				"operationId": "createLiveTradingAccount",
				"summary": "Open a live Axiory trading account under a corporate cTID alias",
				"description": "Async capability that opens a live trading account for the end-user resolved from the verified envelope (never a request field). `Idempotency-Key` is a required HTTP header (consumed by the engine async proxy, never seen by this service). Returns 202 immediately with a `Location` header naming the status path of the request; poll that path, or receive a Standard-Webhooks-v1 signed callback at `callbackUrl` when the app takes callbacks. The status and callback payloads are those of the old account-provisioning routes.",
				"x-purple-target": {
					"type": "async-sfn",
					"resource": "AccountProvisioningLiveSfn"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CreateLiveAccountRequestBody"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Request accepted; account opening proceeds asynchronously. The `Location` header carries the status path of the request.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/AccountProvisioningAcceptedResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/trades": {
			"get": {
				"operationId": "getDemoTrades",
				"summary": "List trades for one of the caller's demo accounts",
				"description": "Returns a cursor-paginated list of trades for the account the accountId names. Cursor is the last positionId from the previous page; omit for first page.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetAccountTradesLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"enum": [
								"OPEN",
								"CLOSED",
								"ALL"
							],
							"default": "ALL"
						},
						"required": false,
						"name": "status",
						"in": "query"
					},
					{
						"schema": {
							"type": "integer",
							"minimum": 0,
							"exclusiveMinimum": true,
							"maximum": 100,
							"default": 20
						},
						"required": false,
						"name": "limit",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"opaque": true
						},
						"required": false,
						"name": "cursor",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Paginated trades (possibly empty)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradesPage"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover it",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not served by this feature — report-server accounts only — or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Report server or account identifiers unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/transactions": {
			"get": {
				"operationId": "getDemoTransactions",
				"summary": "List transactions for one of the caller's demo accounts",
				"description": "Returns a cursor-paginated list of transactions for the account the accountId names. type=balance returns balance-op transactions; type=fee returns fee transactions; omit for all. Cursor is the last transactionId from the previous page.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetAccountTransactionsLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"enum": [
								"balance",
								"fee"
							]
						},
						"required": false,
						"name": "type",
						"in": "query"
					},
					{
						"schema": {
							"type": "integer",
							"minimum": 0,
							"exclusiveMinimum": true,
							"maximum": 100,
							"default": 20
						},
						"required": false,
						"name": "limit",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"opaque": true
						},
						"required": false,
						"name": "cursor",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Paginated transactions (possibly empty)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TransactionsPage"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover it",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not served by this feature — report-server accounts only — or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Report server or account identifiers unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/trades": {
			"get": {
				"operationId": "getLiveTrades",
				"summary": "List trades for one of the caller's live accounts",
				"description": "Returns a cursor-paginated list of trades for the account the accountId names. Cursor is the last positionId from the previous page; omit for first page.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetAccountTradesLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"enum": [
								"OPEN",
								"CLOSED",
								"ALL"
							],
							"default": "ALL"
						},
						"required": false,
						"name": "status",
						"in": "query"
					},
					{
						"schema": {
							"type": "integer",
							"minimum": 0,
							"exclusiveMinimum": true,
							"maximum": 100,
							"default": 20
						},
						"required": false,
						"name": "limit",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"opaque": true
						},
						"required": false,
						"name": "cursor",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Paginated trades (possibly empty)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradesPage"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover it",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not served by this feature — report-server accounts only — or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Report server or account identifiers unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/transactions": {
			"get": {
				"operationId": "getLiveTransactions",
				"summary": "List transactions for one of the caller's live accounts",
				"description": "Returns a cursor-paginated list of transactions for the account the accountId names. type=balance returns balance-op transactions; type=fee returns fee transactions; omit for all. Cursor is the last transactionId from the previous page.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetAccountTransactionsLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"enum": [
								"balance",
								"fee"
							]
						},
						"required": false,
						"name": "type",
						"in": "query"
					},
					{
						"schema": {
							"type": "integer",
							"minimum": 0,
							"exclusiveMinimum": true,
							"maximum": 100,
							"default": 20
						},
						"required": false,
						"name": "limit",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"opaque": true
						},
						"required": false,
						"name": "cursor",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Paginated transactions (possibly empty)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TransactionsPage"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover it",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is not served by this feature — report-server accounts only — or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Report server or account identifiers unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/positions": {
			"get": {
				"operationId": "getDemoTradingPositions",
				"summary": "List open positions for a caller-owned trading account (demo)",
				"description": "Open positions from the service-held FIX session. Creates the session on first use. stopLoss and takeProfit are nullable — an absent broker tag is null, never zero. `divergence` is a three-valued enum (new / matched / diverged) reporting whether local state agreed with the broker across a reconnect. `openedAt` is the original open time for a position known before a reconnect. `asOf` and `confirmedAt` are broker-confirmation times, not serve times. Volumes are in units, not lots. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$"
						},
						"required": false,
						"name": "symbol",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Open positions",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingPositionsResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session connecting, reconciling, reconnecting, owned elsewhere, or its login held by an external client",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/orders": {
			"get": {
				"operationId": "getDemoTradingOrders",
				"summary": "List orders for a caller-owned trading account (demo)",
				"description": "Orders from the service-held FIX session. Creates the session on first use. `status` defaults to `pending` — working orders, including partially filled ones — so an omitted `status` returns a FILTERED list; pass `status=all` for the terminal tail as well. Carries its own `complete` flag: false while the order snapshot is still settling or ended in a timeout, because a caller reading orders never sees the session document and would otherwise read a truncated list as authoritative. Volumes are in units. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"enum": [
								"pending",
								"filled",
								"cancelled",
								"rejected",
								"all"
							],
							"default": "pending"
						},
						"required": false,
						"name": "status",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Orders",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingOrdersResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable in its current state",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			},
			"post": {
				"operationId": "placeDemoTradingOrder",
				"summary": "Place an order on a caller-owned trading account (demo)",
				"description": "Requires an Idempotency-Key. Volume is in UNITS, not lots, matching the broker catalogue. LIMIT requires price, STOP requires stopPrice, GTD requires expireTime; MARKET accepts neither expireTime nor positionId. STOP_LIMIT is not offered — the broker does not support it. BRACKET-ORDER CHECK EXEMPTION: an order carrying a positionId that matches a KNOWN local position skips the position-size and open-position-count checks, because those checks exist to bound new exposure and a bracket order reduces it — applying every check uniformly would reject a protective stop-loss on an account already at its position limit. An UNRECOGNISED positionId grants no exemption and is checked as new exposure; `risk.bracketExemptionApplied` reports which happened. Every broker outcome for a message that was sent — including a reject, a cancel and an expiry — is a 200 carrying `execution.status`; `unknown` means no terminal report arrived in the wait budget, so verify with a read rather than resending. The service never answers a write with a 5xx once a message may have left the socket. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/PlaceStageTradingOrderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Order accepted; see execution.status for the outcome. When the service did not answer inside the hop budget the body is the unknown-outcome partial: `execution.status` is `unknown` and no other field is present",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/PlaceTradingOrderRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, trade lock timeout, or Idempotency-Key reused with a different request",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or the broker refused the message as an order; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable, or the idempotency store is unavailable and the write failed closed; nothing was sent; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			},
			"patch": {
				"operationId": "modifyDemoTradingOrder",
				"summary": "Modify a pending order on a caller-owned trading account (demo)",
				"description": "Requires an Idempotency-Key. Identify the order by orderId (the broker-assigned id) or by clOrdId; exactly one is needed. Volume is in units. The order id rides the body rather than the path. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/ModifyStageTradingOrderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Modified order, or the unknown-outcome partial when the service did not answer inside the hop budget",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ModifyTradingOrderRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: Unknown order for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or broker refusal; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			},
			"delete": {
				"operationId": "cancelDemoTradingOrder",
				"summary": "Cancel a pending order on a caller-owned trading account (demo)",
				"description": "Requires an Idempotency-Key. Identify the order by orderId or clOrdId. The kill switch is still the first check: cancelling a bracket order is not de-risking, so no blanket exemption applies. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CancelStageTradingOrderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Cancelled order, or the unknown-outcome partial when the service did not answer inside the hop budget",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/CancelTradingOrderRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: Unknown order for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or broker refusal; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/quotes": {
			"get": {
				"operationId": "getDemoTradingQuotes",
				"summary": "Read several symbol quotes for a caller-owned trading account (demo)",
				"description": "Subscribes the requested symbols on demand, bounded by the per-session subscription cap, and serves cached ticks. Symbols with no tick yet are listed in `missing` rather than failing the batch. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string"
						},
						"required": true,
						"name": "symbols",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Quotes",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingQuotesResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, unknown symbol, or subscription cap exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Quote connection not serviceable",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/symbols": {
			"get": {
				"operationId": "getDemoTradingSymbols",
				"summary": "List the tradable symbol catalogue for an account (demo)",
				"description": "The TRADABLE subset of the broker symbol catalogue for the server the matched account row names — a symbol the broker has disabled is absent from this list even though a position already open on it can still be read, priced and closed. NON-CREATING: needs no FIX session and never starts one, so polling it cannot resurrect an idle-evicted session. Volumes (lotSize, minVolume, maxVolume, volumeStep) are in units. minVolume and volumeStep are DERIVED as 0.01 lot wherever the broker export omits them, which is every row of both production catalogues today; maxVolume is always null because the broker publishes no per-symbol maximum. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string"
						},
						"required": false,
						"name": "search",
						"in": "query"
					},
					{
						"schema": {
							"type": "string"
						},
						"required": false,
						"name": "assetClass",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Symbol catalogue",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingSymbolsResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/session": {
			"get": {
				"operationId": "getDemoTradingSession",
				"summary": "Read FIX session status for a caller-owned trading account (demo)",
				"description": "Per-connection status for the trade and quote legs. NON-CREATING: never connects, so health polling cannot resurrect idle-evicted sessions or re-claim logins nobody is trading — an absent or evicted session is 404, not a connect. Reports trading readiness and reconciliation completeness as SEPARATE flags: trading activates on the position snapshot alone, so tradingReady true with reconciliationComplete false is normal while orders settle. `positionSnapshotDefinitive` says whether the position snapshot ended on a definitive end-of-snapshot signal or on a fallback timer, in which case it may be truncated. A connection whose login was taken by an external client reports reason=login_taken_by_external_client, distinct from broker_unreachable and from converging, and is user-actionable. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Session status",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingSessionStatusResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: No session held for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session owned by another task and that task could not be reached; retry — nothing about the session was changed",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/positions/close": {
			"post": {
				"operationId": "closeDemoTradingPosition",
				"summary": "Close a position on a caller-owned trading account (demo)",
				"description": "Requires an Idempotency-Key. Omit volume to close the whole position; volume is in units. `position` is null when the close flattened it. Bracket orders linked to the position are cancelled automatically and listed in `cancelledLinkedOrders`. The position id rides the body rather than the path. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CloseStageTradingPositionRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Close accepted; see execution.status. When the service did not answer inside the hop budget the body is the unknown-outcome partial and carries no position or realisedPnl",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/CloseTradingPositionRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: Unknown position for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or broker refusal; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/positions/close-all": {
			"post": {
				"operationId": "closeAllDemoTradingPositions",
				"summary": "Close every open position on a caller-owned trading account (demo)",
				"description": "Requires an Idempotency-Key. Optionally filtered by symbol. Partial failure is a 200 with per-position results in THREE buckets: `closed` (the broker confirmed a fill), `pending` (accepted, no terminal report inside the wait budget — it may well have closed) and `failed` (refused). Two buckets are insufficient: folding `pending` into `closed` reports a close that may not have happened, and folding it into `failed` invites a second close of a position that is already flat. A retried close-all resumes rather than closing twice. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CloseAllStageTradingPositionsRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Per-position results in three buckets. When the service did not answer inside the hop budget the body is instead the unknown-outcome partial — no results and no summary, because this hop never learnt what any leg did",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/CloseAllTradingPositionsRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, kill switch active, or risk check failed; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/session/reconnect": {
			"post": {
				"operationId": "reconnectDemoTradingSession",
				"summary": "Reconnect the FIX session for a caller-owned trading account (demo)",
				"description": "Requires an Idempotency-Key, like every write route — though a reconnect is idempotent by nature, so the durable record behind it is exempt or scoped to seconds rather than kept for days, which would replay a stale acknowledgement to a legitimate reconnect an hour later. The consumer-safe session verb: a reconnect already in progress returns the same acknowledgement rather than restarting anything. With no session held it connects. It never resets the reconnect backoff of a session whose login an external client holds — that state is reported as a conflict instead, because reconnecting cannot win a login back and resetting the backoff would hammer it. The account is addressed by the opaque accountId the demo account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/ReconnectStageTradingSessionRequest"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Reconnect accepted",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ReconnectTradingSessionResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "The login is held by an external client, or the session is owned elsewhere during failover",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/positions": {
			"get": {
				"operationId": "getLiveTradingPositions",
				"summary": "List open positions for a caller-owned trading account (live)",
				"description": "Open positions from the service-held FIX session. Creates the session on first use. stopLoss and takeProfit are nullable — an absent broker tag is null, never zero. `divergence` is a three-valued enum (new / matched / diverged) reporting whether local state agreed with the broker across a reconnect. `openedAt` is the original open time for a position known before a reconnect. `asOf` and `confirmedAt` are broker-confirmation times, not serve times. Volumes are in units, not lots. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$"
						},
						"required": false,
						"name": "symbol",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Open positions",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingPositionsResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session connecting, reconciling, reconnecting, owned elsewhere, or its login held by an external client",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/orders": {
			"get": {
				"operationId": "getLiveTradingOrders",
				"summary": "List orders for a caller-owned trading account (live)",
				"description": "Orders from the service-held FIX session. Creates the session on first use. `status` defaults to `pending` — working orders, including partially filled ones — so an omitted `status` returns a FILTERED list; pass `status=all` for the terminal tail as well. Carries its own `complete` flag: false while the order snapshot is still settling or ended in a timeout, because a caller reading orders never sees the session document and would otherwise read a truncated list as authoritative. Volumes are in units. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string",
							"enum": [
								"pending",
								"filled",
								"cancelled",
								"rejected",
								"all"
							],
							"default": "pending"
						},
						"required": false,
						"name": "status",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Orders",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingOrdersResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable in its current state",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			},
			"post": {
				"operationId": "placeLiveTradingOrder",
				"summary": "Place an order on a caller-owned trading account (live)",
				"description": "Requires an Idempotency-Key. Volume is in UNITS, not lots, matching the broker catalogue. LIMIT requires price, STOP requires stopPrice, GTD requires expireTime; MARKET accepts neither expireTime nor positionId. STOP_LIMIT is not offered — the broker does not support it. BRACKET-ORDER CHECK EXEMPTION: an order carrying a positionId that matches a KNOWN local position skips the position-size and open-position-count checks, because those checks exist to bound new exposure and a bracket order reduces it — applying every check uniformly would reject a protective stop-loss on an account already at its position limit. An UNRECOGNISED positionId grants no exemption and is checked as new exposure; `risk.bracketExemptionApplied` reports which happened. Every broker outcome for a message that was sent — including a reject, a cancel and an expiry — is a 200 carrying `execution.status`; `unknown` means no terminal report arrived in the wait budget, so verify with a read rather than resending. The service never answers a write with a 5xx once a message may have left the socket. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/PlaceStageTradingOrderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Order accepted; see execution.status for the outcome. When the service did not answer inside the hop budget the body is the unknown-outcome partial: `execution.status` is `unknown` and no other field is present",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/PlaceTradingOrderRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, trade lock timeout, or Idempotency-Key reused with a different request",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or the broker refused the message as an order; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable, or the idempotency store is unavailable and the write failed closed; nothing was sent; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			},
			"patch": {
				"operationId": "modifyLiveTradingOrder",
				"summary": "Modify a pending order on a caller-owned trading account (live)",
				"description": "Requires an Idempotency-Key. Identify the order by orderId (the broker-assigned id) or by clOrdId; exactly one is needed. Volume is in units. The order id rides the body rather than the path. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/ModifyStageTradingOrderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Modified order, or the unknown-outcome partial when the service did not answer inside the hop budget",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ModifyTradingOrderRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: Unknown order for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or broker refusal; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			},
			"delete": {
				"operationId": "cancelLiveTradingOrder",
				"summary": "Cancel a pending order on a caller-owned trading account (live)",
				"description": "Requires an Idempotency-Key. Identify the order by orderId or clOrdId. The kill switch is still the first check: cancelling a bracket order is not de-risking, so no blanket exemption applies. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CancelStageTradingOrderRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Cancelled order, or the unknown-outcome partial when the service did not answer inside the hop budget",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/CancelTradingOrderRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: Unknown order for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or broker refusal; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/quotes": {
			"get": {
				"operationId": "getLiveTradingQuotes",
				"summary": "Read several symbol quotes for a caller-owned trading account (live)",
				"description": "Subscribes the requested symbols on demand, bounded by the per-session subscription cap, and serves cached ticks. Symbols with no tick yet are listed in `missing` rather than failing the batch. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string"
						},
						"required": true,
						"name": "symbols",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Quotes",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingQuotesResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, unknown symbol, or subscription cap exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Quote connection not serviceable",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/symbols": {
			"get": {
				"operationId": "getLiveTradingSymbols",
				"summary": "List the tradable symbol catalogue for an account (live)",
				"description": "The TRADABLE subset of the broker symbol catalogue for the server the matched account row names — a symbol the broker has disabled is absent from this list even though a position already open on it can still be read, priced and closed. NON-CREATING: needs no FIX session and never starts one, so polling it cannot resurrect an idle-evicted session. Volumes (lotSize, minVolume, maxVolume, volumeStep) are in units. minVolume and volumeStep are DERIVED as 0.01 lot wherever the broker export omits them, which is every row of both production catalogues today; maxVolume is always null because the broker publishes no per-symbol maximum. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					},
					{
						"schema": {
							"type": "string"
						},
						"required": false,
						"name": "search",
						"in": "query"
					},
					{
						"schema": {
							"type": "string"
						},
						"required": false,
						"name": "assetClass",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Symbol catalogue",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingSymbolsResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/session": {
			"get": {
				"operationId": "getLiveTradingSession",
				"summary": "Read FIX session status for a caller-owned trading account (live)",
				"description": "Per-connection status for the trade and quote legs. NON-CREATING: never connects, so health polling cannot resurrect idle-evicted sessions or re-claim logins nobody is trading — an absent or evicted session is 404, not a connect. Reports trading readiness and reconciliation completeness as SEPARATE flags: trading activates on the position snapshot alone, so tradingReady true with reconciliationComplete false is normal while orders settle. `positionSnapshotDefinitive` says whether the position snapshot ended on a definitive end-of-snapshot signal or on a fallback timer, in which case it may be truncated. A connection whose login was taken by an external client reports reason=login_taken_by_external_client, distinct from broker_unreachable and from converging, and is user-actionable. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"parameters": [
					{
						"schema": {
							"type": "string",
							"minLength": 1,
							"description": "The opaque accountId the account list on this stage returned for the account"
						},
						"required": true,
						"description": "The opaque accountId the account list on this stage returned for the account",
						"name": "accountId",
						"in": "query"
					}
				],
				"responses": {
					"200": {
						"description": "Session status",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingSessionStatusResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: No session held for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session owned by another task and that task could not be reached; retry — nothing about the session was changed",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/positions/close": {
			"post": {
				"operationId": "closeLiveTradingPosition",
				"summary": "Close a position on a caller-owned trading account (live)",
				"description": "Requires an Idempotency-Key. Omit volume to close the whole position; volume is in units. `position` is null when the close flattened it. Bracket orders linked to the position are cancelled automatically and listed in `cancelledLinkedOrders`. The position id rides the body rather than the path. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CloseStageTradingPositionRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Close accepted; see execution.status. When the service did not answer inside the hop budget the body is the unknown-outcome partial and carries no position or realisedPnl",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/CloseTradingPositionRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked. Also: Unknown position for this account",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, risk check failed, kill switch active, or broker refusal; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/positions/close-all": {
			"post": {
				"operationId": "closeAllLiveTradingPositions",
				"summary": "Close every open position on a caller-owned trading account (live)",
				"description": "Requires an Idempotency-Key. Optionally filtered by symbol. Partial failure is a 200 with per-position results in THREE buckets: `closed` (the broker confirmed a fill), `pending` (accepted, no terminal report inside the wait budget — it may well have closed) and `failed` (refused). Two buckets are insufficient: folding `pending` into `closed` reports a close that may not have happened, and folding it into `failed` invites a second close of a position that is already flat. A retried close-all resumes rather than closing twice. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/CloseAllStageTradingPositionsRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Per-position results in three buckets. When the service did not answer inside the hop budget the body is instead the unknown-outcome partial — no results and no summary, because this hop never learnt what any leg did",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/CloseAllTradingPositionsRouteResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "Session not serviceable, lock timeout, or Idempotency-Key reused",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Not a cTrader account, kill switch active, or risk check failed; or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"429": {
						"description": "Per-account order rate limit exceeded",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable or idempotency store down; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/live/session/reconnect": {
			"post": {
				"operationId": "reconnectLiveTradingSession",
				"summary": "Reconnect the FIX session for a caller-owned trading account (live)",
				"description": "Requires an Idempotency-Key, like every write route — though a reconnect is idempotent by nature, so the durable record behind it is exempt or scoped to seconds rather than kept for days, which would replay a stale acknowledgement to a legitimate reconnect an hour later. The consumer-safe session verb: a reconnect already in progress returns the same acknowledgement rather than restarting anything. With no session held it connects. It never resets the reconnect backoff of a session whose login an external client holds — that state is reported as a conflict instead, because reconnecting cannot win a login back and resetting the backoff would hammer it. The account is addressed by the opaque accountId the live account list returned; an account of the other stage is never served on this route.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "TradingExecutionLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/ReconnectStageTradingSessionRequest"
							}
						}
					}
				},
				"responses": {
					"202": {
						"description": "Reconnect accepted",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ReconnectTradingSessionResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error, or Idempotency-Key missing",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, the forwarded scopes do not cover it, or it is outside the restrictions the request carries — one identical response for all of them, so account existence is never leaked",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"409": {
						"description": "The login is held by an external client, or the session is owned elsewhere during failover",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"422": {
						"description": "Account exists but is not a cTrader account (MT4/MT5 not supported); or the request carries a restriction this route cannot apply, or a value it cannot resolve",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Broker unreachable; or account identifiers are unavailable, ownership could not be established because an account source was unreadable, or the engine relied on a default this service no longer applies",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/TradingExecutionError"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-account/demo/credentials/yourbourse": {
			"post": {
				"operationId": "issueDemoAccessToken",
				"summary": "Issue a client-API credential for one of the caller's demo accounts",
				"description": "Mints, through the broker's admin API, a credential the broker's own client API accepts for the demo account the accountId names, and returns it together with the client API address to use it at. YourBourse accounts only: an account on another platform is refused (422), and only after it has been confirmed as the caller's and in scope.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "IssueAccountAccessTokenLambda"
				},
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/StageAccountAccessTokenRequest"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Credential issued: the two signing parts, the broker's own identifier for the credential, its kind, when it stops working, and the client API address",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/AccountAccessTokenResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No account of the caller on this stage answers to this accountId, or the forwarded scopes do not cover it. The same answer for an id that does not decode, carries another brand or stage, or names an account the caller does not hold, so the route cannot be used to discover which accounts exist.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "Account is the caller’s and in scope, and is on a platform this route does not serve; or the request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "Service unavailable: the account source or account identifiers are degraded, the outbound broker budget this route shares with account provisioning is spent for this stage, or the engine relied on a default this service does not apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		},
		"/v1/trading-user": {
			"get": {
				"operationId": "getTradingUser",
				"summary": "Get the caller's own CRM customer record",
				"description": "Returns the authenticated caller's own CRM customer record, resolved via the engine-supplied brandClientId. No caller-supplied id, email, or key is read or trusted. A valid caller with no CRM row receives a 404.",
				"x-purple-target": {
					"type": "lambda",
					"resource": "GetTradingUserLambda"
				},
				"responses": {
					"200": {
						"description": "The caller's CRM record",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/GetTradingUserResponse"
								}
							}
						}
					},
					"400": {
						"description": "Validation error (malformed identity fields)",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"401": {
						"description": "Unauthorized",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"403": {
						"description": "Forbidden",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"404": {
						"description": "No CRM record exists for the caller",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"422": {
						"description": "The request carries a restriction this route cannot apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"500": {
						"description": "Internal Server Error",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					},
					"503": {
						"description": "CRM datastore unreachable, or the engine relied on a default this service does not apply",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/ErrorResponse"
								}
							}
						}
					}
				}
			}
		}
	},
	"components": {
		"securitySchemes": {
			"apiKey": {
				"type": "apiKey",
				"in": "header",
				"name": "x-api-key"
			},
			"bearerAuth": {
				"type": "http",
				"scheme": "bearer",
				"bearerFormat": "JWT"
			}
		},
		"schemas": {
			"ErrorResponse": {
				"type": "object",
				"required": [
					"error"
				],
				"properties": {
					"error": {
						"type": "object",
						"required": [
							"code",
							"message"
						],
						"properties": {
							"code": {
								"type": "string"
							},
							"message": {
								"type": "string"
							},
							"requestId": {
								"type": "string"
							}
						}
					}
				}
			},
			"AppInfo": {
				"type": "object",
				"required": [
					"appId",
					"name",
					"capabilities"
				],
				"properties": {
					"appId": {
						"type": "string",
						"description": "Echoed `app_id` for known apps. Empty string when the request was malformed."
					},
					"name": {
						"type": "string",
						"description": "Display name for the consent screen. Empty string for unknown / invalid / deactivated apps (sentinel)."
					},
					"description": {
						"type": "string"
					},
					"capabilities": {
						"type": "array",
						"description": "Per-scope consent disclosures. Empty array for unknown / invalid / deactivated apps.",
						"items": {
							"type": "object",
							"required": [
								"scope",
								"label",
								"riskLevel"
							],
							"properties": {
								"scope": {
									"type": "string"
								},
								"label": {
									"type": "string"
								},
								"riskLevel": {
									"type": "string",
									"enum": [
										"standard",
										"high"
									]
								},
								"riskWarning": {
									"type": "string"
								}
							}
						}
					}
				}
			},
			"RequestStatusResponse": {
				"type": "object",
				"properties": {
					"requestId": {
						"type": "string"
					},
					"operation": {
						"type": "string",
						"enum": [
							"accounts.create"
						],
						"description": "The capability that created the request: the `accounts.create` scope."
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						],
						"description": "The environment the request was created on."
					},
					"status": {
						"$ref": "#/components/schemas/AccountProvisioningStatusState"
					},
					"platform": {
						"$ref": "#/components/schemas/DemoAccountPlatform"
					},
					"result": {
						"$ref": "#/components/schemas/AccountProvisioningStatusResult"
					},
					"updatedAt": {
						"type": "string",
						"description": "ISO timestamp of the last status transition."
					}
				},
				"required": [
					"requestId",
					"operation",
					"env",
					"status",
					"updatedAt"
				]
			},
			"AccountProvisioningStatusState": {
				"type": "string",
				"enum": [
					"PENDING",
					"SUCCEEDED",
					"FAILED"
				]
			},
			"DemoAccountPlatform": {
				"type": "string",
				"enum": [
					"CTRADER",
					"YOURBOURSE"
				],
				"description": "Target trading platform for a demo account. `CTRADER` bridges to my-axiory and requires the `ctrader` options block; `YOURBOURSE` calls the YourBourse Admin API directly and carries no options block of its own."
			},
			"AccountProvisioningStatusResult": {
				"type": "object",
				"properties": {
					"callbackStatus": {
						"type": "string",
						"enum": [
							"success",
							"error"
						]
					},
					"callbackPayload": {
						"type": "object",
						"additionalProperties": {
							"nullable": true
						},
						"description": "Completion payload, discriminated by the platform the request was opened on. On a cTrader success: the unified account fields + echoed ctid/correlationId; on a cTrader error: the failure code plus an `accountExists` marker (opened-but-failed vs not-opened) + best-effort echoed ctid/correlationId. On a YourBourse success: the account fields YourBourse returns, with no alias field; on a YourBourse error: the failure code plus a three-state `accountOutcome` (created | not_created | unknown) and the account number when one is known. Present on both success and error."
					},
					"error": {
						"type": "object",
						"properties": {
							"code": {
								"type": "string"
							},
							"message": {
								"type": "string"
							}
						},
						"required": [
							"code",
							"message"
						],
						"description": "Typed failure code + human-readable cause. Present on error."
					}
				},
				"required": [
					"callbackStatus"
				]
			},
			"FixCredentialsReadResponse": {
				"oneOf": [
					{
						"$ref": "#/components/schemas/FixCredentialsStored"
					},
					{
						"$ref": "#/components/schemas/FixCredentialsNone"
					}
				],
				"discriminator": {
					"propertyName": "status",
					"mapping": {
						"stored": "#/components/schemas/FixCredentialsStored",
						"none": "#/components/schemas/FixCredentialsNone"
					}
				}
			},
			"FixCredentialsStored": {
				"type": "object",
				"properties": {
					"status": {
						"type": "string",
						"enum": [
							"stored"
						]
					},
					"provisionedAt": {
						"type": "string",
						"description": "ISO timestamp of initial provisioning"
					}
				},
				"required": [
					"status",
					"provisionedAt"
				]
			},
			"FixCredentialsNone": {
				"type": "object",
				"properties": {
					"status": {
						"type": "string",
						"enum": [
							"none"
						]
					}
				},
				"required": [
					"status"
				]
			},
			"FixCredentialsProvisionResponse": {
				"type": "object",
				"properties": {
					"status": {
						"type": "string",
						"enum": [
							"provisioned"
						]
					},
					"provisionedAt": {
						"type": "string",
						"description": "ISO timestamp of provisioning"
					}
				},
				"required": [
					"status",
					"provisionedAt"
				]
			},
			"FixCredentialsProvisionRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"password": {
						"type": "string",
						"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^a-zA-Z\\d\\s])[^\\s]{8,16}$",
						"description": "Optional caller-supplied investor password (plaintext). Omit to have one generated. Must be 8–16 chars with at least one lowercase, uppercase, digit, and symbol, and no whitespace."
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env"
				]
			},
			"FixCredentialsStageProvisionRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"password": {
						"type": "string",
						"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^a-zA-Z\\d\\s])[^\\s]{8,16}$",
						"secret": true,
						"description": "Optional caller-supplied investor password (plaintext). Omit to have one generated. Must be 8–16 chars with at least one lowercase, uppercase, digit, and symbol, and no whitespace."
					}
				},
				"required": [
					"accountId"
				],
				"additionalProperties": false
			},
			"GetAccountsByTraderEmailResponse": {
				"type": "object",
				"properties": {
					"accounts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/TradingAccount"
						}
					}
				},
				"required": [
					"accounts"
				]
			},
			"TradingAccount": {
				"type": "object",
				"properties": {
					"brand": {
						"type": "string",
						"enum": [
							"axiory",
							"purpleTrading",
							"fintokei"
						]
					},
					"accountNumber": {
						"type": "string"
					},
					"traderId": {
						"type": "number"
					},
					"email": {
						"type": "string"
					},
					"platform": {
						"type": "string",
						"enum": [
							"mt4",
							"mt5",
							"ctrader",
							"yourbourse"
						]
					},
					"balance": {
						"type": "number"
					},
					"leverage": {
						"type": "number"
					},
					"group": {
						"type": "string"
					},
					"serverId": {
						"type": "number"
					},
					"serverName": {
						"type": "string"
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"hasOpenPositions": {
						"type": "boolean"
					},
					"currency": {
						"type": "string",
						"enum": [
							"AED",
							"AFN",
							"ALL",
							"AMD",
							"ANG",
							"AOA",
							"ARS",
							"AUD",
							"AWG",
							"AZN",
							"BAM",
							"BBD",
							"BDT",
							"BGN",
							"BHD",
							"BIF",
							"BMD",
							"BND",
							"BOB",
							"BRL",
							"BSD",
							"BTC",
							"BTN",
							"BWP",
							"BYN",
							"BZD",
							"CAD",
							"CDF",
							"CHF",
							"CLF",
							"CLP",
							"CNH",
							"CNY",
							"COP",
							"CRC",
							"CUC",
							"CUP",
							"CVE",
							"CZK",
							"DJF",
							"DKK",
							"DOP",
							"DZD",
							"EGP",
							"ERN",
							"ETB",
							"EUR",
							"FJD",
							"FKP",
							"GBP",
							"GEL",
							"GGP",
							"GHS",
							"GIP",
							"GMD",
							"GNF",
							"GTQ",
							"GYD",
							"HKD",
							"HNL",
							"HRK",
							"HTG",
							"HUF",
							"IDR",
							"ILS",
							"IMP",
							"INR",
							"IQD",
							"IRR",
							"ISK",
							"JEP",
							"JMD",
							"JOD",
							"JPY",
							"KES",
							"KGS",
							"KHR",
							"KMF",
							"KPW",
							"KRW",
							"KWD",
							"KYD",
							"KZT",
							"LAK",
							"LBP",
							"LKR",
							"LRD",
							"LSL",
							"LYD",
							"MAD",
							"MDL",
							"MGA",
							"MKD",
							"MMK",
							"MNT",
							"MOP",
							"MRO",
							"MRU",
							"MUR",
							"MVR",
							"MWK",
							"MXN",
							"MYR",
							"MZN",
							"NAD",
							"NGN",
							"NIO",
							"NOK",
							"NPR",
							"NZD",
							"OMR",
							"PAB",
							"PEN",
							"PGK",
							"PHP",
							"PKR",
							"PLN",
							"PYG",
							"QAR",
							"RON",
							"RSD",
							"RUB",
							"RWF",
							"SAR",
							"SBD",
							"SCR",
							"SDG",
							"SEK",
							"SGD",
							"SHP",
							"SLL",
							"SOS",
							"SRD",
							"SSP",
							"STD",
							"STN",
							"SVC",
							"SYP",
							"SZL",
							"THB",
							"TJS",
							"TMT",
							"TND",
							"TOP",
							"TRY",
							"TTD",
							"TWD",
							"TZS",
							"UAH",
							"UGX",
							"USD",
							"UYU",
							"UZS",
							"VEF",
							"VES",
							"VND",
							"VUV",
							"WST",
							"XAF",
							"XAG",
							"XAU",
							"XCD",
							"XDR",
							"XOF",
							"XPD",
							"XPF",
							"XPT",
							"YER",
							"ZAR",
							"ZMW",
							"ZWL"
						]
					},
					"accountType": {
						"type": "string",
						"nullable": true,
						"enum": [
							"ALPHA",
							"MAX",
							"MAXISL",
							"NANO",
							"NANOISL",
							"STANDARD",
							"STANDARDISL",
							"TAKUMIS",
							"TERA",
							"TERAISL",
							"WALLET",
							"INVESTOR",
							"ZERO",
							"ZEROISL",
							null
						]
					}
				},
				"required": [
					"brand",
					"accountNumber",
					"traderId",
					"email",
					"platform",
					"balance",
					"leverage",
					"group",
					"serverId",
					"serverName",
					"env",
					"hasOpenPositions",
					"currency",
					"accountType"
				]
			},
			"GetStageAccountsResponse": {
				"type": "object",
				"properties": {
					"accounts": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/StageTradingAccount"
						}
					}
				},
				"required": [
					"accounts"
				]
			},
			"StageTradingAccount": {
				"type": "object",
				"properties": {
					"brand": {
						"type": "string",
						"enum": [
							"axiory",
							"purpleTrading",
							"fintokei"
						]
					},
					"accountNumber": {
						"type": "string"
					},
					"traderId": {
						"type": "number"
					},
					"platform": {
						"type": "string",
						"enum": [
							"mt4",
							"mt5",
							"ctrader",
							"yourbourse"
						]
					},
					"balance": {
						"type": "number"
					},
					"leverage": {
						"type": "number"
					},
					"group": {
						"type": "string"
					},
					"serverId": {
						"type": "number"
					},
					"serverName": {
						"type": "string"
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"hasOpenPositions": {
						"type": "boolean"
					},
					"currency": {
						"type": "string",
						"enum": [
							"AED",
							"AFN",
							"ALL",
							"AMD",
							"ANG",
							"AOA",
							"ARS",
							"AUD",
							"AWG",
							"AZN",
							"BAM",
							"BBD",
							"BDT",
							"BGN",
							"BHD",
							"BIF",
							"BMD",
							"BND",
							"BOB",
							"BRL",
							"BSD",
							"BTC",
							"BTN",
							"BWP",
							"BYN",
							"BZD",
							"CAD",
							"CDF",
							"CHF",
							"CLF",
							"CLP",
							"CNH",
							"CNY",
							"COP",
							"CRC",
							"CUC",
							"CUP",
							"CVE",
							"CZK",
							"DJF",
							"DKK",
							"DOP",
							"DZD",
							"EGP",
							"ERN",
							"ETB",
							"EUR",
							"FJD",
							"FKP",
							"GBP",
							"GEL",
							"GGP",
							"GHS",
							"GIP",
							"GMD",
							"GNF",
							"GTQ",
							"GYD",
							"HKD",
							"HNL",
							"HRK",
							"HTG",
							"HUF",
							"IDR",
							"ILS",
							"IMP",
							"INR",
							"IQD",
							"IRR",
							"ISK",
							"JEP",
							"JMD",
							"JOD",
							"JPY",
							"KES",
							"KGS",
							"KHR",
							"KMF",
							"KPW",
							"KRW",
							"KWD",
							"KYD",
							"KZT",
							"LAK",
							"LBP",
							"LKR",
							"LRD",
							"LSL",
							"LYD",
							"MAD",
							"MDL",
							"MGA",
							"MKD",
							"MMK",
							"MNT",
							"MOP",
							"MRO",
							"MRU",
							"MUR",
							"MVR",
							"MWK",
							"MXN",
							"MYR",
							"MZN",
							"NAD",
							"NGN",
							"NIO",
							"NOK",
							"NPR",
							"NZD",
							"OMR",
							"PAB",
							"PEN",
							"PGK",
							"PHP",
							"PKR",
							"PLN",
							"PYG",
							"QAR",
							"RON",
							"RSD",
							"RUB",
							"RWF",
							"SAR",
							"SBD",
							"SCR",
							"SDG",
							"SEK",
							"SGD",
							"SHP",
							"SLL",
							"SOS",
							"SRD",
							"SSP",
							"STD",
							"STN",
							"SVC",
							"SYP",
							"SZL",
							"THB",
							"TJS",
							"TMT",
							"TND",
							"TOP",
							"TRY",
							"TTD",
							"TWD",
							"TZS",
							"UAH",
							"UGX",
							"USD",
							"UYU",
							"UZS",
							"VEF",
							"VES",
							"VND",
							"VUV",
							"WST",
							"XAF",
							"XAG",
							"XAU",
							"XCD",
							"XDR",
							"XOF",
							"XPD",
							"XPF",
							"XPT",
							"YER",
							"ZAR",
							"ZMW",
							"ZWL"
						]
					},
					"accountType": {
						"type": "string",
						"nullable": true,
						"enum": [
							"ALPHA",
							"MAX",
							"MAXISL",
							"NANO",
							"NANOISL",
							"STANDARD",
							"STANDARDISL",
							"TAKUMIS",
							"TERA",
							"TERAISL",
							"WALLET",
							"INVESTOR",
							"ZERO",
							"ZEROISL",
							null
						]
					},
					"accountId": {
						"type": "string",
						"description": "Opaque identifier of the account on this stage. Deterministic per account, bound to the brand and the stage, and the only way the other routes of this stage address it."
					}
				},
				"required": [
					"brand",
					"accountNumber",
					"traderId",
					"platform",
					"balance",
					"leverage",
					"group",
					"serverId",
					"serverName",
					"env",
					"hasOpenPositions",
					"currency",
					"accountType",
					"accountId"
				]
			},
			"TradesPage": {
				"type": "object",
				"properties": {
					"items": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Trade"
						}
					},
					"nextCursor": {
						"type": "string",
						"nullable": true
					},
					"hasMore": {
						"type": "boolean"
					}
				},
				"required": [
					"items",
					"nextCursor",
					"hasMore"
				]
			},
			"Trade": {
				"type": "object",
				"properties": {
					"brand": {
						"type": "string",
						"enum": [
							"axiory",
							"purpleTrading",
							"fintokei"
						]
					},
					"positionId": {
						"type": "number"
					},
					"openedAt": {
						"type": "string"
					},
					"closedAt": {
						"type": "string"
					},
					"side": {
						"type": "string",
						"enum": [
							"LONG",
							"SHORT"
						]
					},
					"volume": {
						"type": "number"
					},
					"lot": {
						"type": "number"
					},
					"symbolId": {
						"type": "string"
					},
					"stopLoss": {
						"type": "number"
					},
					"takeProfit": {
						"type": "number"
					},
					"openPrice": {
						"type": "number"
					},
					"closePrice": {
						"type": "number"
					},
					"swap": {
						"type": "number"
					},
					"commission": {
						"type": "number"
					},
					"profit": {
						"type": "number"
					},
					"onCredit": {
						"type": "boolean"
					},
					"status": {
						"type": "string",
						"enum": [
							"OPEN",
							"CLOSED",
							"ALL"
						]
					}
				},
				"required": [
					"brand",
					"positionId",
					"openedAt",
					"side",
					"symbolId",
					"onCredit",
					"status"
				]
			},
			"TransactionsPage": {
				"type": "object",
				"properties": {
					"items": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Transaction"
						}
					},
					"nextCursor": {
						"type": "string",
						"nullable": true
					},
					"hasMore": {
						"type": "boolean"
					}
				},
				"required": [
					"items",
					"nextCursor",
					"hasMore"
				]
			},
			"Transaction": {
				"type": "object",
				"properties": {
					"transactionId": {
						"type": "number"
					},
					"createdAt": {
						"type": "string"
					},
					"transactionType": {
						"type": "string",
						"enum": [
							"deposit",
							"withdrawal",
							"credit",
							"other"
						]
					},
					"comment": {
						"type": "string",
						"nullable": true
					},
					"amount": {
						"type": "number"
					},
					"feeDeducted": {
						"type": "boolean"
					}
				},
				"required": [
					"transactionId",
					"createdAt",
					"transactionType",
					"comment",
					"amount",
					"feeDeducted"
				]
			},
			"AccountProvisioningAcceptedResponse": {
				"type": "object",
				"properties": {
					"requestId": {
						"type": "string"
					}
				},
				"required": [
					"requestId"
				]
			},
			"CreateLiveAccountRequest": {
				"type": "object",
				"properties": {
					"ctrader": {
						"$ref": "#/components/schemas/CtraderRequestOptions"
					},
					"correlationId": {
						"type": "string",
						"minLength": 1,
						"description": "Caller's correlation id, echoed back verbatim inside the terminal callback payload."
					},
					"callbackUrl": {
						"type": "string",
						"format": "uri",
						"description": "Callback delivery URL. Consumed and stripped by the engine async proxy before the request reaches this service — axi-services never reads this field itself; documented here only because the published contract mirrors the wire contract."
					},
					"platform": {
						"type": "string",
						"enum": [
							"CTRADER"
						],
						"description": "Target trading platform. Pinned to `CTRADER` for this contract version — the bridge only opens cTrader accounts today. Platform-specific fields are never flat on the body; they live under the key matching the lowercased platform value (`ctrader`)."
					},
					"accountType": {
						"type": "string",
						"minLength": 1,
						"description": "Strategy Builder fintech account type. Server-side grouping is driven by this value; the specific enum is pending product/FinTech input."
					},
					"alwaysMaxLeverage": {
						"type": "boolean",
						"description": "Whether the account always trades at max leverage. Required — a request omitting this field is rejected at the gateway with a 400 naming the field."
					},
					"maxLeverage": {
						"type": "string",
						"minLength": 1,
						"description": "Opaque leverage-tier identifier (my-axiory's Leverage enum, e.g. 'Leverage100'). Optional."
					},
					"leverage": {
						"type": "string",
						"minLength": 1,
						"description": "Opaque leverage-tier identifier (my-axiory's Leverage enum, e.g. 'Leverage100'). Required when `alwaysMaxLeverage` is false."
					},
					"currency": {
						"type": "string",
						"minLength": 1,
						"description": "Account currency code."
					}
				},
				"required": [
					"ctrader",
					"correlationId",
					"callbackUrl",
					"platform",
					"accountType",
					"alwaysMaxLeverage",
					"currency"
				]
			},
			"CtraderRequestOptions": {
				"type": "object",
				"properties": {
					"ctid": {
						"type": "string",
						"format": "email",
						"description": "Corporate cTrader ID alias email (app-held credentials pool). Substitutes the fintech-api account-creation email to drive cTID attach; my-axiory rewrites it to the end-user's email in-SFN before completion."
					}
				},
				"required": [
					"ctid"
				],
				"description": "cTrader-specific request options. Required whenever `platform` is `CTRADER`, and forbidden for any other platform — a rule enforced by this service (see `backend/shared/validation.ts`), because OpenAPI 3.0 cannot express conditional requiredness."
			},
			"CreateDemoAccountRequest": {
				"type": "object",
				"properties": {
					"ctrader": {
						"$ref": "#/components/schemas/CtraderDemoRequestOptions"
					},
					"correlationId": {
						"type": "string",
						"minLength": 1,
						"description": "Caller's correlation id, echoed back verbatim inside the terminal callback payload."
					},
					"callbackUrl": {
						"type": "string",
						"format": "uri",
						"description": "Callback delivery URL. Consumed and stripped by the engine async proxy before the request reaches this service — axi-services never reads this field itself; documented here only because the published contract mirrors the wire contract."
					},
					"platform": {
						"$ref": "#/components/schemas/DemoAccountPlatform"
					},
					"deposit": {
						"type": "number",
						"description": "Initial demo deposit amount. Range validation happens later, in-SFN — this layer only requires the field to be a number."
					},
					"leverage": {
						"type": "string",
						"minLength": 1,
						"description": "Opaque leverage-tier identifier (my-axiory's Leverage enum, e.g. 'Leverage100'). Required for demo accounts — DemoAccountOpeningSMInput.data.leverage is a required field and account opening throws without it."
					},
					"currency": {
						"type": "string",
						"minLength": 1,
						"description": "Account currency code."
					}
				},
				"required": [
					"correlationId",
					"callbackUrl",
					"platform",
					"deposit",
					"leverage",
					"currency"
				]
			},
			"CtraderDemoRequestOptions": {
				"type": "object",
				"properties": {
					"ctid": {
						"type": "string",
						"format": "email",
						"description": "Corporate cTrader ID alias email (app-held credentials pool). Substitutes the fintech-api account-creation email to drive cTID attach; my-axiory rewrites it to the end-user's email in-SFN before completion."
					},
					"accountType": {
						"type": "string",
						"minLength": 1,
						"description": "Strategy Builder fintech account type. Server-side grouping is driven by this value; the specific enum is pending product/FinTech input."
					},
					"alwaysMaxLeverage": {
						"type": "boolean",
						"description": "Whether the account always trades at max leverage. Required inside this block — a `CTRADER` request whose block omits it is rejected at the gateway with a 400 naming the field."
					}
				},
				"required": [
					"ctid",
					"accountType",
					"alwaysMaxLeverage"
				],
				"description": "cTrader-specific demo request options. Required whenever the demo `platform` is `CTRADER`, and forbidden for any other platform — a rule enforced by this service (see `backend/shared/validation.ts`), because OpenAPI 3.0 cannot express conditional requiredness."
			},
			"AccountProvisioningStatusResponse": {
				"type": "object",
				"properties": {
					"requestId": {
						"type": "string"
					},
					"status": {
						"$ref": "#/components/schemas/AccountProvisioningStatusState"
					},
					"platform": {
						"$ref": "#/components/schemas/DemoAccountPlatform"
					},
					"result": {
						"$ref": "#/components/schemas/AccountProvisioningStatusResult"
					},
					"updatedAt": {
						"type": "string",
						"description": "ISO timestamp of the last status transition."
					}
				},
				"required": [
					"requestId",
					"status",
					"updatedAt"
				]
			},
			"CreateDemoAccountRequestBody": {
				"type": "object",
				"properties": {
					"platform": {
						"$ref": "#/components/schemas/DemoAccountRequestPlatform"
					},
					"ctrader": {
						"$ref": "#/components/schemas/DemoAccountRequestCtraderOptions"
					},
					"correlationId": {
						"type": "string",
						"minLength": 1,
						"description": "Caller's correlation id, echoed back verbatim inside the terminal callback payload."
					},
					"callbackUrl": {
						"type": "string",
						"format": "uri",
						"description": "Callback delivery URL. Required of a first-party app and refused for a self-service app by the engine async proxy, which consumes and strips it before the request reaches this service."
					},
					"deposit": {
						"type": "number",
						"description": "Initial demo deposit amount. Range validation happens later, in-SFN."
					},
					"leverage": {
						"$ref": "#/components/schemas/AccountRequestLeverage"
					},
					"currency": {
						"$ref": "#/components/schemas/AccountRequestCurrency"
					}
				},
				"required": [
					"platform",
					"correlationId",
					"deposit",
					"leverage",
					"currency"
				],
				"additionalProperties": false
			},
			"DemoAccountRequestPlatform": {
				"type": "string",
				"enum": [
					"ctrader",
					"yourbourse"
				],
				"description": "Target trading platform, spelled as the account list spells it. `ctrader` bridges to my-axiory and requires the `ctrader` options block; `yourbourse` calls the YourBourse Admin API directly and carries no options block."
			},
			"DemoAccountRequestCtraderOptions": {
				"type": "object",
				"properties": {
					"ctid": {
						"type": "string",
						"format": "email",
						"description": "Corporate cTrader ID alias email (app-held credentials pool). Substitutes the fintech-api account-creation email to drive cTID attach; my-axiory rewrites it to the end-user's email in-SFN before completion."
					},
					"accountType": {
						"$ref": "#/components/schemas/AccountRequestAccountType"
					},
					"alwaysMaxLeverage": {
						"type": "boolean",
						"description": "Whether the account always trades at max leverage."
					}
				},
				"required": [
					"ctid",
					"accountType",
					"alwaysMaxLeverage"
				],
				"additionalProperties": false,
				"description": "cTrader-specific demo request options. Required whenever `platform` is `ctrader`, and refused for any other platform — enforced by this service, because OpenAPI 3.0 cannot express conditional requiredness.",
				"requiredWhen": {
					"platform": "ctrader"
				}
			},
			"AccountRequestAccountType": {
				"type": "string",
				"enum": [
					"NANO",
					"STANDARD",
					"MAX",
					"ZERO",
					"NANOISL",
					"STANDARDISL",
					"MAXISL",
					"ZEROISL",
					"WALLET",
					"TAKUMIS"
				],
				"description": "Strategy Builder fintech account type, spelled as the account list spells it."
			},
			"AccountRequestLeverage": {
				"anyOf": [
					{
						"type": "number",
						"enum": [
							1
						]
					},
					{
						"type": "number",
						"enum": [
							10
						]
					},
					{
						"type": "number",
						"enum": [
							25
						]
					},
					{
						"type": "number",
						"enum": [
							50
						]
					},
					{
						"type": "number",
						"enum": [
							100
						]
					},
					{
						"type": "number",
						"enum": [
							200
						]
					},
					{
						"type": "number",
						"enum": [
							300
						]
					},
					{
						"type": "number",
						"enum": [
							400
						]
					},
					{
						"type": "number",
						"enum": [
							500
						]
					},
					{
						"type": "number",
						"enum": [
							600
						]
					},
					{
						"type": "number",
						"enum": [
							777
						]
					},
					{
						"type": "number",
						"enum": [
							800
						]
					},
					{
						"type": "number",
						"enum": [
							1000
						]
					},
					{
						"type": "number",
						"enum": [
							2000
						]
					}
				],
				"description": "Leverage ratio, as the number after `1:`. Mapped to my-axiory leverage tiers inside this service."
			},
			"AccountRequestCurrency": {
				"type": "string",
				"enum": [
					"AED",
					"AFN",
					"ALL",
					"AMD",
					"ANG",
					"AOA",
					"ARS",
					"AUD",
					"AWG",
					"AZN",
					"BAM",
					"BBD",
					"BDT",
					"BGN",
					"BHD",
					"BIF",
					"BMD",
					"BND",
					"BOB",
					"BRL",
					"BSD",
					"BTC",
					"BTN",
					"BWP",
					"BYN",
					"BZD",
					"CAD",
					"CDF",
					"CHF",
					"CLF",
					"CLP",
					"CNH",
					"CNY",
					"COP",
					"CRC",
					"CUC",
					"CUP",
					"CVE",
					"CZK",
					"DJF",
					"DKK",
					"DOP",
					"DZD",
					"EGP",
					"ERN",
					"ETB",
					"EUR",
					"FJD",
					"FKP",
					"GBP",
					"GEL",
					"GGP",
					"GHS",
					"GIP",
					"GMD",
					"GNF",
					"GTQ",
					"GYD",
					"HKD",
					"HNL",
					"HRK",
					"HTG",
					"HUF",
					"IDR",
					"ILS",
					"IMP",
					"INR",
					"IQD",
					"IRR",
					"ISK",
					"JEP",
					"JMD",
					"JOD",
					"JPY",
					"KES",
					"KGS",
					"KHR",
					"KMF",
					"KPW",
					"KRW",
					"KWD",
					"KYD",
					"KZT",
					"LAK",
					"LBP",
					"LKR",
					"LRD",
					"LSL",
					"LYD",
					"MAD",
					"MDL",
					"MGA",
					"MKD",
					"MMK",
					"MNT",
					"MOP",
					"MRO",
					"MRU",
					"MUR",
					"MVR",
					"MWK",
					"MXN",
					"MYR",
					"MZN",
					"NAD",
					"NGN",
					"NIO",
					"NOK",
					"NPR",
					"NZD",
					"OMR",
					"PAB",
					"PEN",
					"PGK",
					"PHP",
					"PKR",
					"PLN",
					"PYG",
					"QAR",
					"RON",
					"RSD",
					"RUB",
					"RWF",
					"SAR",
					"SBD",
					"SCR",
					"SDG",
					"SEK",
					"SGD",
					"SHP",
					"SLL",
					"SOS",
					"SRD",
					"SSP",
					"STD",
					"STN",
					"SVC",
					"SYP",
					"SZL",
					"THB",
					"TJS",
					"TMT",
					"TND",
					"TOP",
					"TRY",
					"TTD",
					"TWD",
					"TZS",
					"UAH",
					"UGX",
					"USD",
					"UYU",
					"UZS",
					"VEF",
					"VES",
					"VND",
					"VUV",
					"WST",
					"XAF",
					"XAG",
					"XAU",
					"XCD",
					"XDR",
					"XOF",
					"XPD",
					"XPF",
					"XPT",
					"YER",
					"ZAR",
					"ZMW",
					"ZWL"
				],
				"description": "Account currency, one of the currency codes the report server records."
			},
			"CreateLiveAccountRequestBody": {
				"type": "object",
				"properties": {
					"platform": {
						"$ref": "#/components/schemas/LiveAccountRequestPlatform"
					},
					"ctrader": {
						"$ref": "#/components/schemas/LiveAccountRequestCtraderOptions"
					},
					"correlationId": {
						"type": "string",
						"minLength": 1,
						"description": "Caller's correlation id, echoed back verbatim inside the terminal callback payload."
					},
					"callbackUrl": {
						"type": "string",
						"format": "uri",
						"description": "Callback delivery URL. Required of a first-party app and refused for a self-service app by the engine async proxy, which consumes and strips it before the request reaches this service."
					},
					"accountType": {
						"$ref": "#/components/schemas/AccountRequestAccountType"
					},
					"alwaysMaxLeverage": {
						"type": "boolean",
						"description": "Whether the account always trades at max leverage."
					},
					"maxLeverage": {
						"$ref": "#/components/schemas/AccountRequestLeverage"
					},
					"leverage": {
						"anyOf": [
							{
								"type": "number",
								"enum": [
									1
								]
							},
							{
								"type": "number",
								"enum": [
									10
								]
							},
							{
								"type": "number",
								"enum": [
									25
								]
							},
							{
								"type": "number",
								"enum": [
									50
								]
							},
							{
								"type": "number",
								"enum": [
									100
								]
							},
							{
								"type": "number",
								"enum": [
									200
								]
							},
							{
								"type": "number",
								"enum": [
									300
								]
							},
							{
								"type": "number",
								"enum": [
									400
								]
							},
							{
								"type": "number",
								"enum": [
									500
								]
							},
							{
								"type": "number",
								"enum": [
									600
								]
							},
							{
								"type": "number",
								"enum": [
									777
								]
							},
							{
								"type": "number",
								"enum": [
									800
								]
							},
							{
								"type": "number",
								"enum": [
									1000
								]
							},
							{
								"type": "number",
								"enum": [
									2000
								]
							}
						],
						"description": "Leverage the account opens at. Required when `alwaysMaxLeverage` is false; ignored downstream when it is true, because the account then opens at the client’s maximum.",
						"requiredWhen": {
							"alwaysMaxLeverage": false
						}
					},
					"currency": {
						"$ref": "#/components/schemas/AccountRequestCurrency"
					}
				},
				"required": [
					"platform",
					"ctrader",
					"correlationId",
					"accountType",
					"alwaysMaxLeverage",
					"currency"
				],
				"additionalProperties": false
			},
			"LiveAccountRequestPlatform": {
				"type": "string",
				"enum": [
					"ctrader"
				],
				"description": "Target trading platform, spelled as the account list spells it. Live accounts open on cTrader only; platform-specific fields live under the `ctrader` key."
			},
			"LiveAccountRequestCtraderOptions": {
				"type": "object",
				"properties": {
					"ctid": {
						"type": "string",
						"format": "email",
						"description": "Corporate cTrader ID alias email (app-held credentials pool). Substitutes the fintech-api account-creation email to drive cTID attach; my-axiory rewrites it to the end-user's email in-SFN before completion."
					}
				},
				"required": [
					"ctid"
				],
				"additionalProperties": false,
				"description": "cTrader-specific live request options."
			},
			"TradingPositionsResponse": {
				"type": "object",
				"properties": {
					"positions": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Position"
						}
					},
					"asOf": {
						"type": "string"
					}
				},
				"required": [
					"positions",
					"asOf"
				]
			},
			"Position": {
				"type": "object",
				"properties": {
					"positionId": {
						"type": "string"
					},
					"symbol": {
						"type": "string"
					},
					"side": {
						"$ref": "#/components/schemas/OrderSide"
					},
					"volume": {
						"type": "number"
					},
					"entryPrice": {
						"type": "number"
					},
					"currentPrice": {
						"type": "number",
						"nullable": true
					},
					"unrealisedPnl": {
						"$ref": "#/components/schemas/Money"
					},
					"swap": {
						"type": "number",
						"nullable": true
					},
					"commission": {
						"type": "number",
						"nullable": true
					},
					"stopLoss": {
						"type": "number",
						"nullable": true
					},
					"takeProfit": {
						"type": "number",
						"nullable": true
					},
					"openedAt": {
						"type": "string"
					},
					"linkedOrderIds": {
						"type": "array",
						"items": {
							"type": "string"
						}
					},
					"divergence": {
						"$ref": "#/components/schemas/PositionDivergence"
					},
					"divergenceNote": {
						"type": "string",
						"nullable": true
					},
					"confirmedAt": {
						"type": "string"
					}
				},
				"required": [
					"positionId",
					"symbol",
					"side",
					"volume",
					"entryPrice",
					"currentPrice",
					"unrealisedPnl",
					"swap",
					"commission",
					"stopLoss",
					"takeProfit",
					"openedAt",
					"linkedOrderIds",
					"divergence",
					"divergenceNote",
					"confirmedAt"
				]
			},
			"OrderSide": {
				"type": "string",
				"enum": [
					"BUY",
					"SELL"
				]
			},
			"Money": {
				"type": "object",
				"properties": {
					"amount": {
						"type": "number"
					},
					"currency": {
						"type": "string"
					},
					"converted": {
						"type": "object",
						"nullable": true,
						"properties": {
							"amount": {
								"type": "number"
							},
							"currency": {
								"type": "string",
								"enum": [
									"USD"
								]
							},
							"rate": {
								"type": "number"
							},
							"rateSource": {
								"type": "string"
							}
						},
						"required": [
							"amount",
							"currency",
							"rate",
							"rateSource"
						]
					}
				},
				"required": [
					"amount",
					"currency",
					"converted"
				]
			},
			"PositionDivergence": {
				"type": "string",
				"enum": [
					"new",
					"matched",
					"diverged"
				]
			},
			"TradingExecutionError": {
				"type": "object",
				"properties": {
					"code": {
						"$ref": "#/components/schemas/TradingExecutionErrorCode"
					},
					"message": {
						"type": "string"
					},
					"details": {
						"$ref": "#/components/schemas/TradingExecutionErrorDetails"
					}
				},
				"required": [
					"code",
					"message"
				]
			},
			"TradingExecutionErrorCode": {
				"type": "string",
				"enum": [
					"validation_error",
					"idempotency_key_required",
					"account_not_owned",
					"scope_insufficient",
					"env_not_permitted",
					"operator_only",
					"platform_not_supported",
					"order_not_found",
					"position_not_found",
					"session_not_found",
					"session_connecting",
					"session_reconciling",
					"session_reconnecting",
					"session_owned_elsewhere",
					"session_login_conflict",
					"idempotency_key_reused",
					"lock_timeout",
					"risk_check_failed",
					"kill_switch_active",
					"broker_rejected",
					"rate_limited",
					"broker_unavailable",
					"idempotency_store_unavailable",
					"account_not_found",
					"restriction_not_applicable",
					"restriction_value_unresolvable",
					"assumed_default_mismatch",
					"account_id_unavailable",
					"account_unavailable"
				]
			},
			"TradingExecutionErrorDetails": {
				"type": "object",
				"properties": {
					"field": {
						"type": "string"
					},
					"retryAfterMs": {
						"type": "integer",
						"minimum": 0
					},
					"check": {
						"$ref": "#/components/schemas/RiskCheckName"
					},
					"limit": {
						"type": "number"
					},
					"actual": {
						"type": "number"
					},
					"reason": {
						"type": "string",
						"enum": [
							"daily_loss",
							"circuit_breaker"
						]
					},
					"resetsAt": {
						"type": "string"
					},
					"brokerReason": {
						"type": "string"
					},
					"action": {
						"type": "string",
						"enum": [
							"retry",
							"disconnect_other_client",
							"contact_support"
						]
					}
				}
			},
			"RiskCheckName": {
				"type": "string",
				"enum": [
					"kill_switch",
					"rate_limit",
					"symbol_whitelist",
					"position_size",
					"open_positions",
					"daily_loss",
					"volume_validation"
				]
			},
			"TradingOrdersResponse": {
				"type": "object",
				"properties": {
					"orders": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Order"
						}
					},
					"complete": {
						"type": "boolean"
					},
					"asOf": {
						"type": "string"
					}
				},
				"required": [
					"orders",
					"complete",
					"asOf"
				]
			},
			"Order": {
				"type": "object",
				"properties": {
					"orderId": {
						"type": "string"
					},
					"clOrdId": {
						"type": "string"
					},
					"symbol": {
						"type": "string"
					},
					"side": {
						"$ref": "#/components/schemas/OrderSide"
					},
					"type": {
						"$ref": "#/components/schemas/OrderType"
					},
					"timeInForce": {
						"$ref": "#/components/schemas/TimeInForce"
					},
					"volume": {
						"type": "number"
					},
					"filledVolume": {
						"type": "number"
					},
					"price": {
						"type": "number",
						"nullable": true
					},
					"stopPrice": {
						"type": "number",
						"nullable": true
					},
					"positionId": {
						"type": "string",
						"nullable": true
					},
					"status": {
						"$ref": "#/components/schemas/OrderStatus"
					},
					"placedBy": {
						"type": "string",
						"nullable": true
					},
					"createdAt": {
						"type": "string"
					},
					"updatedAt": {
						"type": "string"
					},
					"expireTime": {
						"type": "string",
						"nullable": true
					},
					"confirmedAt": {
						"type": "string"
					}
				},
				"required": [
					"orderId",
					"clOrdId",
					"symbol",
					"side",
					"type",
					"timeInForce",
					"volume",
					"filledVolume",
					"price",
					"stopPrice",
					"positionId",
					"status",
					"placedBy",
					"createdAt",
					"updatedAt",
					"expireTime",
					"confirmedAt"
				]
			},
			"OrderType": {
				"type": "string",
				"enum": [
					"MARKET",
					"LIMIT",
					"STOP"
				]
			},
			"TimeInForce": {
				"type": "string",
				"enum": [
					"GTC",
					"DAY",
					"IOC",
					"FOK",
					"GTD"
				]
			},
			"OrderStatus": {
				"type": "string",
				"enum": [
					"pending",
					"partiallyFilled",
					"filled",
					"cancelled",
					"rejected",
					"expired"
				]
			},
			"TradingQuoteResponse": {
				"type": "object",
				"properties": {
					"symbol": {
						"type": "string"
					},
					"bid": {
						"type": "number"
					},
					"ask": {
						"type": "number"
					},
					"spread": {
						"type": "number"
					},
					"confirmedAt": {
						"type": "string"
					},
					"stale": {
						"type": "boolean"
					}
				},
				"required": [
					"symbol",
					"bid",
					"ask",
					"spread",
					"confirmedAt",
					"stale"
				]
			},
			"TradingQuotesResponse": {
				"type": "object",
				"properties": {
					"quotes": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Quote"
						}
					},
					"missing": {
						"type": "array",
						"items": {
							"type": "string"
						}
					}
				},
				"required": [
					"quotes",
					"missing"
				]
			},
			"Quote": {
				"type": "object",
				"properties": {
					"symbol": {
						"type": "string"
					},
					"bid": {
						"type": "number"
					},
					"ask": {
						"type": "number"
					},
					"spread": {
						"type": "number"
					},
					"confirmedAt": {
						"type": "string"
					},
					"stale": {
						"type": "boolean"
					}
				},
				"required": [
					"symbol",
					"bid",
					"ask",
					"spread",
					"confirmedAt",
					"stale"
				]
			},
			"TradingSymbolsResponse": {
				"type": "object",
				"properties": {
					"symbols": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/TradingSymbol"
						}
					}
				},
				"required": [
					"symbols"
				]
			},
			"TradingSymbol": {
				"type": "object",
				"properties": {
					"name": {
						"type": "string"
					},
					"id": {
						"type": "integer"
					},
					"description": {
						"type": "string",
						"nullable": true
					},
					"assetClass": {
						"type": "string",
						"nullable": true
					},
					"assetClassGroup": {
						"type": "string",
						"nullable": true,
						"enum": [
							"fx",
							"indices",
							"commodities",
							"other",
							null
						]
					},
					"lotSize": {
						"type": "number"
					},
					"minVolume": {
						"type": "number"
					},
					"maxVolume": {
						"type": "number",
						"nullable": true
					},
					"volumeStep": {
						"type": "number"
					},
					"digits": {
						"type": "integer",
						"nullable": true
					},
					"quoteCurrency": {
						"type": "string",
						"nullable": true
					}
				},
				"required": [
					"name",
					"id",
					"description",
					"assetClass",
					"assetClassGroup",
					"lotSize",
					"minVolume",
					"maxVolume",
					"volumeStep",
					"digits",
					"quoteCurrency"
				]
			},
			"TradingSessionStatusResponse": {
				"type": "object",
				"properties": {
					"trade": {
						"$ref": "#/components/schemas/SessionConnection"
					},
					"quote": {
						"$ref": "#/components/schemas/SessionConnection"
					},
					"tradingReady": {
						"type": "boolean"
					},
					"reconciliationComplete": {
						"type": "boolean"
					},
					"orderReconciliation": {
						"type": "string",
						"enum": [
							"complete",
							"timeout",
							"pending"
						]
					},
					"positionSnapshotDefinitive": {
						"type": "boolean"
					},
					"subscribedSymbols": {
						"type": "array",
						"items": {
							"type": "string"
						}
					},
					"subscribedSymbolCap": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"idleSince": {
						"type": "string",
						"nullable": true
					},
					"risk": {
						"type": "object",
						"properties": {
							"killSwitch": {
								"type": "object",
								"properties": {
									"active": {
										"type": "boolean"
									},
									"reason": {
										"type": "string",
										"nullable": true,
										"enum": [
											"daily_loss",
											"circuit_breaker",
											null
										]
									},
									"resetsAt": {
										"type": "string",
										"nullable": true
									}
								},
								"required": [
									"active",
									"reason",
									"resetsAt"
								]
							},
							"dailyPnl": {
								"$ref": "#/components/schemas/Money"
							}
						},
						"required": [
							"killSwitch",
							"dailyPnl"
						]
					},
					"asOf": {
						"type": "string"
					}
				},
				"required": [
					"trade",
					"quote",
					"tradingReady",
					"reconciliationComplete",
					"orderReconciliation",
					"positionSnapshotDefinitive",
					"subscribedSymbols",
					"subscribedSymbolCap",
					"idleSince",
					"risk",
					"asOf"
				]
			},
			"SessionConnection": {
				"type": "object",
				"properties": {
					"state": {
						"$ref": "#/components/schemas/SessionState"
					},
					"reason": {
						"type": "string",
						"nullable": true,
						"enum": [
							"login_taken_by_external_client",
							"broker_unreachable",
							"converging",
							null
						]
					},
					"connectedAt": {
						"type": "string",
						"nullable": true
					},
					"lastHeartbeatAt": {
						"type": "string",
						"nullable": true
					},
					"reconnectAttempts": {
						"type": "integer",
						"minimum": 0
					},
					"nextRetryAt": {
						"type": "string",
						"nullable": true
					}
				},
				"required": [
					"state",
					"reason",
					"connectedAt",
					"lastHeartbeatAt",
					"reconnectAttempts",
					"nextRetryAt"
				]
			},
			"SessionState": {
				"type": "string",
				"enum": [
					"Absent",
					"Connecting",
					"ActiveReconciling",
					"Active",
					"Reconnecting",
					"DisconnectedConflict",
					"BrokerUnavailable",
					"IdleEvicted"
				]
			},
			"PlaceTradingOrderRouteResponse": {
				"anyOf": [
					{
						"$ref": "#/components/schemas/PlaceTradingOrderResponse"
					},
					{
						"$ref": "#/components/schemas/UnknownTradingExecutionOutcome"
					}
				]
			},
			"PlaceTradingOrderResponse": {
				"type": "object",
				"properties": {
					"order": {
						"$ref": "#/components/schemas/Order"
					},
					"execution": {
						"$ref": "#/components/schemas/Execution"
					},
					"risk": {
						"$ref": "#/components/schemas/RiskOutcome"
					}
				},
				"required": [
					"order",
					"execution",
					"risk"
				]
			},
			"Execution": {
				"type": "object",
				"properties": {
					"status": {
						"$ref": "#/components/schemas/ExecutionStatus"
					},
					"clOrdId": {
						"type": "string"
					},
					"orderId": {
						"type": "string",
						"nullable": true
					},
					"fills": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Fill"
						}
					},
					"filledVolume": {
						"type": "number"
					},
					"positionId": {
						"type": "string",
						"nullable": true
					},
					"rejectReason": {
						"type": "string",
						"nullable": true
					}
				},
				"required": [
					"status",
					"clOrdId",
					"orderId",
					"fills",
					"filledVolume",
					"positionId",
					"rejectReason"
				]
			},
			"ExecutionStatus": {
				"type": "string",
				"enum": [
					"filled",
					"partiallyFilled",
					"pending",
					"rejected",
					"cancelled",
					"expired",
					"unknown"
				]
			},
			"Fill": {
				"type": "object",
				"properties": {
					"volume": {
						"type": "number"
					},
					"price": {
						"type": "number"
					},
					"executedAt": {
						"type": "string"
					}
				},
				"required": [
					"volume",
					"price",
					"executedAt"
				]
			},
			"RiskOutcome": {
				"type": "object",
				"properties": {
					"checksPassed": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/RiskCheckName"
						}
					},
					"bracketExemptionApplied": {
						"type": "boolean"
					}
				},
				"required": [
					"checksPassed",
					"bracketExemptionApplied"
				]
			},
			"UnknownTradingExecutionOutcome": {
				"type": "object",
				"properties": {
					"execution": {
						"type": "object",
						"properties": {
							"status": {
								"type": "string",
								"enum": [
									"unknown"
								]
							},
							"clOrdId": {
								"type": "string"
							},
							"orderId": {
								"type": "string",
								"nullable": true
							},
							"fills": {
								"type": "array",
								"items": {
									"$ref": "#/components/schemas/Fill"
								}
							},
							"filledVolume": {
								"type": "number"
							},
							"positionId": {
								"type": "string",
								"nullable": true
							},
							"rejectReason": {
								"type": "string",
								"nullable": true
							}
						},
						"required": [
							"status",
							"clOrdId",
							"orderId",
							"fills",
							"filledVolume",
							"positionId",
							"rejectReason"
						]
					}
				},
				"required": [
					"execution"
				]
			},
			"PlaceTradingOrderRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"symbol": {
						"type": "string",
						"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$"
					},
					"side": {
						"$ref": "#/components/schemas/OrderSide"
					},
					"type": {
						"$ref": "#/components/schemas/OrderType"
					},
					"timeInForce": {
						"$ref": "#/components/schemas/TimeInForce"
					},
					"volume": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"price": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"stopPrice": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"positionId": {
						"type": "string"
					},
					"comment": {
						"type": "string",
						"maxLength": 100
					},
					"expireTime": {
						"type": "string",
						"format": "date-time"
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env",
					"symbol",
					"side",
					"type",
					"volume"
				]
			},
			"ModifyTradingOrderRouteResponse": {
				"anyOf": [
					{
						"$ref": "#/components/schemas/ModifyTradingOrderResponse"
					},
					{
						"$ref": "#/components/schemas/UnknownTradingExecutionOutcome"
					}
				]
			},
			"ModifyTradingOrderResponse": {
				"type": "object",
				"properties": {
					"order": {
						"$ref": "#/components/schemas/Order"
					},
					"execution": {
						"$ref": "#/components/schemas/Execution"
					}
				},
				"required": [
					"order",
					"execution"
				]
			},
			"ModifyTradingOrderRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"orderId": {
						"type": "string"
					},
					"clOrdId": {
						"type": "string"
					},
					"volume": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"price": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"stopPrice": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env"
				]
			},
			"CancelTradingOrderRouteResponse": {
				"anyOf": [
					{
						"$ref": "#/components/schemas/CancelTradingOrderResponse"
					},
					{
						"$ref": "#/components/schemas/UnknownTradingExecutionOutcome"
					}
				]
			},
			"CancelTradingOrderResponse": {
				"type": "object",
				"properties": {
					"order": {
						"$ref": "#/components/schemas/Order"
					},
					"execution": {
						"$ref": "#/components/schemas/Execution"
					}
				},
				"required": [
					"order",
					"execution"
				]
			},
			"CancelTradingOrderRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"orderId": {
						"type": "string"
					},
					"clOrdId": {
						"type": "string"
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env"
				]
			},
			"CloseTradingPositionRouteResponse": {
				"anyOf": [
					{
						"$ref": "#/components/schemas/CloseTradingPositionResponse"
					},
					{
						"$ref": "#/components/schemas/UnknownTradingExecutionOutcome"
					}
				]
			},
			"CloseTradingPositionResponse": {
				"type": "object",
				"properties": {
					"position": {
						"type": "object",
						"nullable": true,
						"properties": {
							"positionId": {
								"type": "string"
							},
							"symbol": {
								"type": "string"
							},
							"side": {
								"$ref": "#/components/schemas/OrderSide"
							},
							"volume": {
								"type": "number"
							},
							"entryPrice": {
								"type": "number"
							},
							"currentPrice": {
								"type": "number",
								"nullable": true
							},
							"unrealisedPnl": {
								"$ref": "#/components/schemas/Money"
							},
							"swap": {
								"type": "number",
								"nullable": true
							},
							"commission": {
								"type": "number",
								"nullable": true
							},
							"stopLoss": {
								"type": "number",
								"nullable": true
							},
							"takeProfit": {
								"type": "number",
								"nullable": true
							},
							"openedAt": {
								"type": "string"
							},
							"linkedOrderIds": {
								"type": "array",
								"items": {
									"type": "string"
								}
							},
							"divergence": {
								"$ref": "#/components/schemas/PositionDivergence"
							},
							"divergenceNote": {
								"type": "string",
								"nullable": true
							},
							"confirmedAt": {
								"type": "string"
							}
						},
						"required": [
							"positionId",
							"symbol",
							"side",
							"volume",
							"entryPrice",
							"currentPrice",
							"unrealisedPnl",
							"swap",
							"commission",
							"stopLoss",
							"takeProfit",
							"openedAt",
							"linkedOrderIds",
							"divergence",
							"divergenceNote",
							"confirmedAt"
						]
					},
					"execution": {
						"$ref": "#/components/schemas/Execution"
					},
					"realisedPnl": {
						"$ref": "#/components/schemas/Money"
					},
					"cancelledLinkedOrders": {
						"type": "array",
						"items": {
							"type": "string"
						}
					}
				},
				"required": [
					"position",
					"execution",
					"realisedPnl",
					"cancelledLinkedOrders"
				]
			},
			"CloseTradingPositionRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"positionId": {
						"type": "string"
					},
					"volume": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"comment": {
						"type": "string",
						"maxLength": 100
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env",
					"positionId"
				]
			},
			"CloseAllTradingPositionsRouteResponse": {
				"anyOf": [
					{
						"$ref": "#/components/schemas/CloseAllTradingPositionsResponse"
					},
					{
						"$ref": "#/components/schemas/UnknownTradingExecutionOutcome"
					}
				]
			},
			"CloseAllTradingPositionsResponse": {
				"type": "object",
				"properties": {
					"results": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/ClosePositionResult"
						}
					},
					"summary": {
						"type": "object",
						"properties": {
							"closed": {
								"type": "integer",
								"minimum": 0
							},
							"pending": {
								"type": "integer",
								"minimum": 0
							},
							"failed": {
								"type": "integer",
								"minimum": 0
							}
						},
						"required": [
							"closed",
							"pending",
							"failed"
						]
					},
					"asOf": {
						"type": "string"
					}
				},
				"required": [
					"results",
					"summary",
					"asOf"
				]
			},
			"ClosePositionResult": {
				"type": "object",
				"properties": {
					"positionId": {
						"type": "string"
					},
					"symbol": {
						"type": "string"
					},
					"outcome": {
						"$ref": "#/components/schemas/ClosePositionOutcome"
					},
					"execution": {
						"type": "object",
						"nullable": true,
						"properties": {
							"status": {
								"$ref": "#/components/schemas/ExecutionStatus"
							},
							"clOrdId": {
								"type": "string"
							},
							"orderId": {
								"type": "string",
								"nullable": true
							},
							"fills": {
								"type": "array",
								"items": {
									"$ref": "#/components/schemas/Fill"
								}
							},
							"filledVolume": {
								"type": "number"
							},
							"positionId": {
								"type": "string",
								"nullable": true
							},
							"rejectReason": {
								"type": "string",
								"nullable": true
							}
						},
						"required": [
							"status",
							"clOrdId",
							"orderId",
							"fills",
							"filledVolume",
							"positionId",
							"rejectReason"
						]
					},
					"realisedPnl": {
						"type": "object",
						"nullable": true,
						"properties": {
							"amount": {
								"type": "number"
							},
							"currency": {
								"type": "string"
							},
							"converted": {
								"type": "object",
								"nullable": true,
								"properties": {
									"amount": {
										"type": "number"
									},
									"currency": {
										"type": "string",
										"enum": [
											"USD"
										]
									},
									"rate": {
										"type": "number"
									},
									"rateSource": {
										"type": "string"
									}
								},
								"required": [
									"amount",
									"currency",
									"rate",
									"rateSource"
								]
							}
						},
						"required": [
							"amount",
							"currency",
							"converted"
						]
					},
					"error": {
						"type": "object",
						"nullable": true,
						"properties": {
							"code": {
								"$ref": "#/components/schemas/TradingExecutionErrorCode"
							},
							"message": {
								"type": "string"
							},
							"details": {
								"$ref": "#/components/schemas/TradingExecutionErrorDetails"
							}
						},
						"required": [
							"code",
							"message"
						]
					}
				},
				"required": [
					"positionId",
					"symbol",
					"outcome",
					"execution",
					"realisedPnl",
					"error"
				]
			},
			"ClosePositionOutcome": {
				"type": "string",
				"enum": [
					"closed",
					"pending",
					"failed"
				]
			},
			"CloseAllTradingPositionsRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"symbol": {
						"type": "string",
						"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$"
					},
					"comment": {
						"type": "string",
						"maxLength": 100
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env"
				]
			},
			"ReconnectTradingSessionResponse": {
				"type": "object",
				"properties": {
					"state": {
						"$ref": "#/components/schemas/SessionState"
					},
					"target": {
						"type": "string",
						"enum": [
							"trade",
							"quote",
							"both"
						]
					},
					"backoffPreserved": {
						"type": "boolean"
					}
				},
				"required": [
					"state",
					"target",
					"backoffPreserved"
				]
			},
			"ReconnectTradingSessionRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					},
					"target": {
						"type": "string",
						"enum": [
							"trade",
							"quote",
							"both"
						],
						"default": "both"
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env"
				]
			},
			"PlaceStageTradingOrderRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"symbol": {
						"type": "string",
						"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$"
					},
					"side": {
						"$ref": "#/components/schemas/OrderSide"
					},
					"type": {
						"$ref": "#/components/schemas/OrderType"
					},
					"timeInForce": {
						"$ref": "#/components/schemas/TimeInForce"
					},
					"volume": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"price": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true,
						"requiredWhen": {
							"type": "LIMIT"
						}
					},
					"stopPrice": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true,
						"requiredWhen": {
							"type": "STOP"
						}
					},
					"positionId": {
						"type": "string"
					},
					"comment": {
						"type": "string",
						"maxLength": 100
					},
					"expireTime": {
						"type": "string",
						"format": "date-time",
						"requiredWhen": {
							"timeInForce": "GTD"
						}
					}
				},
				"required": [
					"accountId",
					"symbol",
					"side",
					"type",
					"volume"
				],
				"additionalProperties": false
			},
			"ModifyStageTradingOrderRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"orderId": {
						"type": "string"
					},
					"clOrdId": {
						"type": "string"
					},
					"volume": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"price": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"stopPrice": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					}
				},
				"required": [
					"accountId"
				],
				"additionalProperties": false
			},
			"CancelStageTradingOrderRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"orderId": {
						"type": "string"
					},
					"clOrdId": {
						"type": "string"
					}
				},
				"required": [
					"accountId"
				],
				"additionalProperties": false
			},
			"CloseStageTradingPositionRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"positionId": {
						"type": "string"
					},
					"volume": {
						"type": "number",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"comment": {
						"type": "string",
						"maxLength": 100
					}
				},
				"required": [
					"accountId",
					"positionId"
				],
				"additionalProperties": false
			},
			"CloseAllStageTradingPositionsRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"symbol": {
						"type": "string",
						"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$"
					},
					"comment": {
						"type": "string",
						"maxLength": 100
					}
				},
				"required": [
					"accountId"
				],
				"additionalProperties": false
			},
			"ReconnectStageTradingSessionRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					},
					"target": {
						"type": "string",
						"enum": [
							"trade",
							"quote",
							"both"
						],
						"default": "both"
					}
				},
				"required": [
					"accountId"
				],
				"additionalProperties": false
			},
			"AccountAccessTokenResponse": {
				"type": "object",
				"properties": {
					"credentialId": {
						"type": "string",
						"description": "The broker's own identifier for this credential. Not the credential itself — safe to log and to use to correlate a later support query with this specific issuance."
					},
					"apiKey": {
						"type": "string",
						"description": "Secret material — the part the client API is told on every request, identifying the session. Transmitted each call, so it is the weaker half. Never appears in a log line, stored state, or an error payload; this success body is the one deliberate exception."
					},
					"signingSecret": {
						"type": "string",
						"description": "Secret material — the key the client signs each request with. It is NEVER transmitted: it keys the signature, and a client that sends it instead of signing with it has disclosed it. Holding both parts is what grants access, so this is the half worth protecting hardest. Never appears in a log line, stored state, or an error payload; this success body is the one deliberate exception."
					},
					"kind": {
						"$ref": "#/components/schemas/AccountAccessCredentialKind"
					},
					"expiresAt": {
						"type": "string",
						"description": "ISO-8601 instant the credential stops working. Nothing in this system shortens or extends that lifetime once the credential is issued."
					},
					"clientApiUrl": {
						"type": "string",
						"format": "uri",
						"description": "The broker's client API address for the stage that served this request. Never derive this from the address this backend calls to mint the credential — the two are configured independently and are not a transform of one another."
					}
				},
				"required": [
					"credentialId",
					"apiKey",
					"signingSecret",
					"kind",
					"expiresAt",
					"clientApiUrl"
				]
			},
			"AccountAccessCredentialKind": {
				"type": "string",
				"enum": [
					"API_KEY_SECRET_PAIR"
				],
				"description": "The shape of the credential material below. `API_KEY_SECRET_PAIR` is two independent secret parts (`apiKey` + `signingSecret`) used together to sign a request against the client API. A future platform whose broker issues, say, a single bearer token would carry a different value here rather than reusing this one."
			},
			"AccountAccessTokenRequest": {
				"type": "object",
				"properties": {
					"serverId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"traderId": {
						"type": "integer",
						"minimum": 0,
						"exclusiveMinimum": true
					},
					"env": {
						"type": "string",
						"enum": [
							"demo",
							"live"
						]
					}
				},
				"required": [
					"serverId",
					"traderId",
					"env"
				]
			},
			"StageAccountAccessTokenRequest": {
				"type": "object",
				"properties": {
					"accountId": {
						"type": "string",
						"minLength": 1,
						"description": "The opaque accountId the account list on this stage returned for the account"
					}
				},
				"required": [
					"accountId"
				],
				"additionalProperties": false
			},
			"GetTradingUserResponse": {
				"type": "object",
				"properties": {
					"user": {
						"$ref": "#/components/schemas/TradingUserCrm"
					}
				},
				"required": [
					"user"
				]
			},
			"TradingUserCrm": {
				"type": "object",
				"properties": {
					"brand": {
						"type": "string",
						"enum": [
							"axiory",
							"purpleTrading",
							"fintokei"
						]
					},
					"brandClientId": {
						"type": "string",
						"minLength": 1
					},
					"email": {
						"type": "string"
					},
					"firstName": {
						"type": "string",
						"nullable": true
					},
					"lastName": {
						"type": "string",
						"nullable": true
					},
					"userStatus": {
						"type": "string",
						"enum": [
							"active",
							"pending",
							"closed",
							"dormant"
						]
					},
					"language": {
						"type": "string",
						"nullable": true
					},
					"partnerCode": {
						"type": "string",
						"nullable": true
					},
					"country": {
						"type": "string",
						"nullable": true
					},
					"dateOfBirth": {
						"type": "string",
						"nullable": true
					},
					"registeredDate": {
						"type": "string",
						"nullable": true
					},
					"phone": {
						"type": "string",
						"nullable": true
					},
					"completedStage": {
						"type": "string",
						"nullable": true
					},
					"gracePeriodStatus": {
						"type": "string",
						"nullable": true
					},
					"crmWorkspace": {
						"type": "string",
						"nullable": true,
						"enum": [
							"asia",
							"glob",
							"tradit",
							"mena",
							null
						]
					}
				},
				"required": [
					"brand",
					"brandClientId",
					"email",
					"userStatus"
				]
			}
		}
	},
	"security": [
		{
			"apiKey": []
		},
		{
			"bearerAuth": []
		}
	]
}