{
  "openapi": "3.1.0",
  "x-mystocks-sandbox-default": true,
  "info": {
    "title": "MyStocks Africa Partner API",
    "version": "1",
    "description": "REST API for embedding African and global stock trading into your product. Get delayed and historical market data, stock prices, and tickers for major African exchanges including:\n- Nairobi Stock Exchange (NSE)\n- Nigerian Exchange Group (NGX)\n- Johannesburg Stock Exchange (JSE)\n- Ghana Stock Exchange (GSE)\n- Bourse Régionale des Valeurs Mobilières (BRVM)\n- Lusaka Securities Exchange (LuSE)\n- Uganda Securities Exchange (USE)\n- Dar es Salaam Stock Exchange (DSE)\n- Zimbabwe Stock Exchange (ZSE)\n- Botswana Stock Exchange (BSE)\n- Egyptian Exchange (EGX)\n- Malawi Stock Exchange (MSE)\n- Casablanca Stock Exchange (CSE)\n- Stock Exchange of Mauritius (SEM)\n\nEasily fetch delayed stock prices, quote trade fees, execute orders, create USD-denominated wallets, handle partner-asserted KYC, subscribe to treasury bills, corporate/government bonds, money market/yield funds, and pre-IPO deals, and subscribe to webhooks for automated dividend and transaction processing. Ideal for B2B fintechs, neobanks, wealth managers, and AI agents.\n\n\n## Market Data & Freshness\nThe market data served here is the **same exchange-supplied, 15-minute-delayed feed** that powers the MyStocks consumer app — there is no separate lower-priority partner feed. A price you read from `/stocks`, `/market/quotes`, or `/stocks/{symbol}` is the identical value shown to retail users, with **no partner markup** (any markup you configure is applied only at trade execution, never to the displayed feed).\n- **African equity prices are 15-minute delayed, not real-time.** MyStocks holds full data rights for every supported exchange; the delay is the exchange feed's latency class, not a licensing restriction. The API refresh target is 900 seconds during the published coverage window, and values are finalized by an end-of-day close. Delivery is **pull-based** — poll `/market/quotes` (batch up to 50 symbols per call) for the latest values and display `asOf`.\n- **Charts** have a universal EOD close baseline for every listed African equity. Complete reported OHLCV is instrument-dependent; use `/stocks/{symbol}/candles` and follow `recommendedChartType` and the quality metadata.\n- **Near-real-time event push** is available via the SSE `/stream` endpoint and webhooks, which carry *events* — order fills, quote expiry, market open/close, dividends — **not** tick-by-tick price streaming.\n- **News & intelligence** (`/companies/{symbol}/news`, `/market-intel`) is MyStocks' own curated editorial feed plus corporate actions — the same content shown in the consumer app, not a high-volume third-party newswire.\n- **History depth** varies by exchange; a few smaller markets accumulate history forward from listing rather than carrying a long back-history. Inspect `historyConfidence` on stock records.\n\n\n## Environments\n| Environment | Base URL | Key Prefix |\n|---|---|---|\n| Sandbox | https://mystocks.africa/api/sandbox/v1/partner | sk_sandbox_ |\n| Production | https://mystocks.africa/api/v1/partner | pk_live_ |\n\n> **Note:** `POST /register` and `POST /reset` are sandbox-only utilities that live at\n> `https://mystocks.africa/api/sandbox/v1` (without the `/partner` segment).\n> All other sandbox endpoints use the `/partner` base above.\n> Sandbox sub-accounts may trade with synthetic data before KYC is asserted so integrations can reach a first fill\n> immediately. Production sub-accounts still require partner-asserted KYC and return `403 KYC_REQUIRED` until verified.\n\n> **Don't confuse the two \"sandbox\" concepts:** the **Sandbox environment** above is a separate\n> base URL used with `sk_sandbox_` keys. New integrations use **Integration Test Tools** at the sandbox\n> base (`/test-tools/*`) with an `sk_sandbox_` key. Production-base `/test-tools/*` is restricted legacy\n> compatibility for specifically approved partners and also moves virtual funds only. The former\n> `/sandbox/*` aliases are deprecated.\n\n## Authentication\nPass your key via **either** header:\n`Authorization: Bearer sk_sandbox_KEY`  or  `x-api-key: sk_sandbox_KEY`\n\n> ⚠️ **Avoid shipping API keys inside client apps** — including the read-only `pk_data_` data key.\n> A key embedded in a mobile or web bundle can be extracted by anyone; data keys share the parent\n> full key's rate-limit bucket, so an extracted key can exhaust your quota. Only one `pk_data_` key\n> is active per partner, and rotating it breaks every shipped install at once. For production apps,\n> proxy market data through your backend, or mint short-lived tokens via `POST /oauth/token` for\n> direct browser use (e.g. `EventSource` on `/stream`). Reserve embedded `pk_data_` keys for\n> low-stakes surfaces such as public widgets.\n\n## Idempotency\nPass `Idempotency-Key` on every authenticated `POST`, `PATCH`, `PUT`, and `DELETE`. The only exceptions are sandbox `register` and `reset`, partner `apply` and `upgrade-request`, and `oauth/token`.\nKeys are deduplicated for 24 h. HTTP 409 if a concurrent duplicate is in-flight.\n\n## Rate Limits\n| Tier | req/min |\n|---|---|\n| Sandbox | 300 |\n| Starter | 100 |\n| Growth | 500 |\n| Enterprise | 2,000 |\n\nEvery authenticated **sandbox and production** response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`. `Retry-After` is included on `429` responses.\n\nRate limiting uses a **1-minute sliding window**. Exceeding the limit returns `429` immediately —\nrequests are not queued. Use `Retry-After` or `X-RateLimit-Reset` to determine when to retry.\n\n## Errors\nAll errors return a structured JSON response of the form `{\"error\": {\"code\": \"ERROR_CODE\", \"message\": \"readable message\"}}`.\n\nCommon HTTP statuses:\n`400` bad params · `401` invalid key · `403` forbidden · `404` not found ·\n`409` idempotency conflict · `422` business rule · `429` rate limit · `500` server error.\n\n## Sandbox Scenario Simulation\nWhen developing against the **Sandbox environment** (`sk_sandbox_...`), you can programmatically force mock status codes and specific sub-error scenarios on demand. This allows you to test your application's error-handling and resilience mechanisms under edge cases.\n\nPass either of these custom headers in your Sandbox requests — they work on **every authenticated sandbox endpoint**:\n*   `X-Sandbox-Force-Status`: Force a specific HTTP status code (e.g., `429`, `400`, `403`, `500`).\n*   `X-Sandbox-Force-Error`: Force a machine-readable sub-code returned in the `code` field (e.g., `INSUFFICIENT_FUNDS`, `RATE_LIMITED`, `KYC_REQUIRED`).\n",
    "contact": {
      "name": "MyStocks Africa Partner Support",
      "email": "partnerships@mystocks.africa",
      "url": "https://mystocks.africa/partners"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://mystocks.africa/terms"
    }
  },
  "servers": [
    {
      "url": "https://mystocks.africa/api/v1/partner",
      "description": "Production"
    },
    {
      "url": "https://mystocks.africa/api/sandbox/v1/partner",
      "description": "Sandbox"
    }
  ],
  "tags": [
    {
      "name": "Registration",
      "description": "Sandbox account creation, partner status, and tier upgrades."
    },
    {
      "name": "Market Data",
      "description": "Stocks, companies, prices, charts, news, and pulse events across 14 African exchanges. Supports delayed and historical stock price feeds for NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LuSE, EGX, DSE, USE, MSE, CSE, and SEM."
    },
    {
      "name": "Trading",
      "description": "Quote, place, list and cancel BUY/SELL orders on your own partner account."
    },
    {
      "name": "Asset Classes",
      "description": "Bonds, treasury bills, funds, ETFs, private credit and pre-IPO opportunities."
    },
    {
      "name": "Sub-Accounts",
      "description": "Provision, fund, trade on behalf of, and report on your end-users. Each sub-account has an isolated USD wallet, portfolio, and order history."
    },
    {
      "name": "Reports",
      "description": "AUM, positions, fees, revenue, invoices and dividends across all sub-accounts."
    },
    {
      "name": "Fund Flow",
      "description": "Top-up and payout requests for your master wallet."
    },
    {
      "name": "Enterprise Security",
      "description": "IP allowlists, scoped keys, signed requests, mTLS gateway enforcement, key rotation, and security evidence."
    },
    {
      "name": "Certification",
      "description": "Sandbox-to-production checklist, golden-path tests, concurrency tests, webhook retry tests, failover drills, and go-live approval."
    },
    {
      "name": "Webhooks",
      "description": "Register HTTPS endpoints for real-time event delivery.\n\n### Webhook Security & Verification\nEvery payload sent to your webhook is signed with an `HMAC-SHA256` signature using your endpoint's configured signing secret. The signature is sent via the `x-mystocks-signature` header.\n\nYou **must** verify the signature on your server to guarantee the request originated from MyStocks Africa and prevent body tampering. Use a constant-time comparison to mitigate timing attacks.\n\n#### Express / Node.js Verification Example\n```javascript\nconst crypto = require('crypto');\n\napp.post('/webhooks/mystocks', express.raw({ type: 'application/json' }), (req, res) => {\n  const signature = req.headers['x-mystocks-signature'];\n  const payload = req.body;\n  const expectedSignature = crypto\n    .createHmac('sha256', process.env.MYSTOCKS_WEBHOOK_SECRET)\n    .update(payload)\n    .digest('hex');\n\n  // Prevent timing attacks using constant-time comparison\n  const verified = crypto.timingSafeEqual(\n    Buffer.from(signature, 'utf8'),\n    Buffer.from(expectedSignature, 'utf8')\n  );\n\n  if (!verified) return res.status(401).send('Invalid signature');\n  const event = JSON.parse(payload.toString('utf8')); // parse only after verification\n  // Handle event safely...\n  res.status(200).send('OK');\n});\n```\n\n#### FastAPI / Python Verification Example\n```python\nimport hmac\nimport hashlib\nimport os\nfrom fastapi import FastAPI, Header, HTTPException, Request\n\napp = FastAPI()\n\n@app.post(\"/webhooks/mystocks\")\nasync def handle_webhook(request: Request, x_mystocks_signature: str = Header(...)):\n    payload = await request.body()\n    expected_sig = hmac.new(\n        key=bytes(os.getenv(\"MYSTOCKS_WEBHOOK_SECRET\"), \"utf-8\"),\n        msg=payload,\n        digestmod=hashlib.sha256\n    ).hexdigest()\n\n    if not hmac.compare_digest(x_mystocks_signature, expected_sig):\n        raise HTTPException(status_code=401, detail=\"Invalid signature\")\n    # Handle event safely...\n    return {\"status\": \"ok\"}\n```\n"
    },
    {
      "name": "Market Intelligence",
      "description": "Editorial market news and exchange announcements feed."
    },
    {
      "name": "Dividends",
      "description": "Dividend calendar and per-account distribution history."
    },
    {
      "name": "Corporate Governance",
      "description": "Corporate actions, record-date entitlements, elections, shareholder meetings, proxy ballots, votes, and custodian evidence."
    },
    {
      "name": "Partner Administration",
      "description": "Partner organization members, roles, invitations, sessions, and human access controls."
    },
    {
      "name": "Partner Support",
      "description": "Partner help cases, SLA clocks, support conversations, references, and escalation evidence."
    },
    {
      "name": "Key Management",
      "description": "Rotate, revoke, and issue read-only data API keys."
    },
    {
      "name": "Observability",
      "description": "Audit log and daily API usage analytics."
    },
    {
      "name": "SLA",
      "description": "Real-time service health and partner SLA tier commitments."
    },
    {
      "name": "Settings",
      "description": "Partner configuration including logo and custom SMTP email."
    },
    {
      "name": "Integration Test Tools",
      "description": "New integrations use `/test-tools/*` on the Sandbox base (`https://mystocks.africa/api/sandbox/v1/partner`) with `sk_sandbox_` keys. Production-base `/test-tools/*` remains restricted legacy compatibility for specifically approved partners and moves virtual funds only. The legacy `/sandbox/*` paths are deprecated aliases.\n"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    },
    {
      "ApiKeyHeader": []
    }
  ],
  "webhooks": {
    "order.pending": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Order submitted — pending live fill",
        "description": "Fired immediately when a trade is submitted via POST /users/{userId}/trade or POST /trade.\nFunds are reserved in the sub-account wallet but execution has not yet occurred.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "order.pending"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "orderId": {
                        "type": "string"
                      },
                      "subAccountId": {
                        "type": "string",
                        "description": "Present for sub-account orders. Omitted for master-account orders."
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "accountType": {
                        "type": "string",
                        "example": "MASTER",
                        "description": "Present only for master-account orders."
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "BUY",
                          "SELL"
                        ]
                      },
                      "symbol": {
                        "type": "string",
                        "example": "SCOM.KE"
                      },
                      "quantity": {
                        "type": "number"
                      },
                      "priceAtOrder": {
                        "type": "number",
                        "description": "Local-currency price at order time."
                      },
                      "usdPriceAtOrder": {
                        "type": "number"
                      },
                      "gross": {
                        "type": "number"
                      },
                      "baseFee": {
                        "type": "number"
                      },
                      "partnerMarkupFee": {
                        "type": "number"
                      },
                      "fee": {
                        "type": "number",
                        "description": "Total fee (baseFee + partnerMarkupFee)."
                      },
                      "totalCost": {
                        "type": "number",
                        "description": "BUY only: gross + fee, deducted from wallet."
                      },
                      "status": {
                        "type": "string",
                        "example": "PENDING"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "order.triggered": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Resting order triggered — routed to MyStocks internal-book execution",
        "description": "Fired when a resting LIMIT/STOP/STOP_LIMIT order's trigger price is crossed during market hours.\nThe order moves from status WORKING to PENDING for automatic MyStocks internal-book execution; `order.pending`\nfires immediately after with the same order.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "order.triggered"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "orderId": {
                        "type": "string"
                      },
                      "clientOrderId": {
                        "type": "string",
                        "nullable": true
                      },
                      "subAccountId": {
                        "type": "string"
                      },
                      "orderType": {
                        "type": "string",
                        "enum": [
                          "LIMIT",
                          "STOP",
                          "STOP_LIMIT"
                        ]
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "BUY",
                          "SELL"
                        ]
                      },
                      "symbol": {
                        "type": "string",
                        "example": "SCOM.KE"
                      },
                      "quantity": {
                        "type": "number"
                      },
                      "limitPrice": {
                        "type": "number",
                        "nullable": true,
                        "description": "Local-currency limit price, if set."
                      },
                      "stopPrice": {
                        "type": "number",
                        "nullable": true,
                        "description": "Local-currency stop price, if set."
                      },
                      "triggerPriceLocal": {
                        "type": "number",
                        "description": "Live local-currency price that crossed the trigger."
                      },
                      "status": {
                        "type": "string",
                        "example": "PENDING"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "order.filled": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Order executed — shares/proceeds credited",
        "description": "Fired when MyStocks ops approves and settles an order.\nAlso fires the legacy alias `trade.settled` with the same payload for backwards compatibility.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "order.filled"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "orderId": {
                        "type": "string"
                      },
                      "subAccountId": {
                        "type": "string",
                        "nullable": true
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "accountType": {
                        "type": "string",
                        "example": "MASTER",
                        "description": "Present only for master-account orders."
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "BUY",
                          "SELL"
                        ]
                      },
                      "symbol": {
                        "type": "string",
                        "example": "SCOM.KE"
                      },
                      "exchange": {
                        "type": "string",
                        "nullable": true
                      },
                      "quantity": {
                        "type": "number"
                      },
                      "priceAtOrder": {
                        "type": "number"
                      },
                      "usdPriceAtOrder": {
                        "type": "number"
                      },
                      "feeAmount": {
                        "type": "number"
                      },
                      "status": {
                        "type": "string",
                        "example": "FILLED"
                      },
                      "settledAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "settlementUsdPrice": {
                        "type": "number"
                      },
                      "totalCost": {
                        "type": "number",
                        "description": "BUY only."
                      },
                      "proceeds": {
                        "type": "number",
                        "description": "SELL only."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "order.rejected": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Order declined by MyStocks ops",
        "description": "Fired when an order is rejected. BUY order funds are returned to the sub-account wallet before this fires.\nAlso fires the legacy alias `trade.rejected` with the same payload.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "order.rejected"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "orderId": {
                        "type": "string"
                      },
                      "subAccountId": {
                        "type": "string",
                        "nullable": true
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "BUY",
                          "SELL"
                        ]
                      },
                      "symbol": {
                        "type": "string"
                      },
                      "exchange": {
                        "type": "string",
                        "nullable": true
                      },
                      "quantity": {
                        "type": "number"
                      },
                      "priceAtOrder": {
                        "type": "number"
                      },
                      "usdPriceAtOrder": {
                        "type": "number"
                      },
                      "feeAmount": {
                        "type": "number"
                      },
                      "status": {
                        "type": "string",
                        "example": "REJECTED"
                      },
                      "settledAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "rejectionCode": {
                        "type": "string",
                        "nullable": true,
                        "example": "LIQUIDITY_UNAVAILABLE"
                      },
                      "rejectionReason": {
                        "type": "string",
                        "nullable": true
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "order.cancelled": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Order cancelled by partner",
        "description": "Fired when a partner cancels a PENDING order via DELETE /orders/{orderId}.\nBUY cancellations include `refunded` (amount returned to sub-account wallet).\nSELL cancellations have no wallet impact and omit `refunded`.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "order.cancelled"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "orderId": {
                        "type": "string"
                      },
                      "subAccountId": {
                        "type": "string"
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "BUY",
                          "SELL"
                        ]
                      },
                      "symbol": {
                        "type": "string"
                      },
                      "quantity": {
                        "type": "number"
                      },
                      "status": {
                        "type": "string",
                        "example": "CANCELLED"
                      },
                      "refunded": {
                        "type": "number",
                        "description": "BUY only: amount returned to sub-account wallet."
                      },
                      "currency": {
                        "type": "string",
                        "example": "USD"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "deposit.confirmed": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Sub-account deposit recorded",
        "description": "Fired after funds are moved from the partner master wallet to a sub-account wallet.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "deposit.confirmed"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "subAccountId": {
                        "type": "string"
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "amount": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "example": "USD"
                      },
                      "newBalance": {
                        "type": "number"
                      },
                      "localAmount": {
                        "type": "number",
                        "description": "Optional: amount in user local currency."
                      },
                      "localCurrency": {
                        "type": "string",
                        "description": "Optional: ISO currency code, e.g. KES."
                      },
                      "fxRate": {
                        "type": "number",
                        "description": "Optional: exchange rate applied."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "withdraw.confirmed": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Sub-account withdrawal processed",
        "description": "Fired after a sub-account withdrawal moves funds back to the partner master wallet.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "withdraw.confirmed"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "subAccountId": {
                        "type": "string"
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "amount": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "example": "USD"
                      },
                      "newBalance": {
                        "type": "number"
                      },
                      "localAmount": {
                        "type": "number",
                        "description": "Optional."
                      },
                      "localCurrency": {
                        "type": "string",
                        "description": "Optional."
                      },
                      "fxRate": {
                        "type": "number",
                        "description": "Optional."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "wallet.credited": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Partner master wallet top-up confirmed",
        "description": "Fired when MyStocks ops credits the partner master wallet after a top-up request is fulfilled.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "wallet.credited"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "amount": {
                        "type": "number"
                      },
                      "newBalance": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "example": "USD"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "topup.confirmed": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Master-account top-up approved and credited",
        "description": "Fired the moment MyStocks ops approves a POST /topup remittance and credits the master float,\nso your backend can resume funding sub-accounts without polling GET /float.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "topup.confirmed"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "requestId": {
                        "type": "string",
                        "description": "The top-up request ID returned by POST /topup."
                      },
                      "amount": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "example": "USD"
                      },
                      "paymentReference": {
                        "type": "string",
                        "nullable": true
                      },
                      "newMasterBalance": {
                        "type": "number",
                        "description": "Master wallet balance after the credit."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "float.low": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Master float dropped below your low-balance threshold",
        "description": "Edge-triggered when a sub-account deposit takes the master balance below the\n`lowBalanceThresholdUsd` you set via PATCH /float. Fires once per crossing (not on every\ndeposit while below). Remit funds via POST /topup to avoid end-user deposits being blocked.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "float.low"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "masterBalanceUsd": {
                        "type": "number"
                      },
                      "lowBalanceThresholdUsd": {
                        "type": "number"
                      },
                      "creditLimitUsd": {
                        "type": "number"
                      },
                      "availableToFundUsd": {
                        "type": "number",
                        "description": "masterBalanceUsd + creditLimitUsd."
                      },
                      "currency": {
                        "type": "string",
                        "example": "USD"
                      },
                      "message": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "kyc.updated": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Sub-account KYC status changed",
        "description": "Fired when a sub-account's KYC status changes — either via POST /users/{userId}/kyc\nor when overridden by MyStocks compliance. `overriddenByAdmin: true` appears only on admin overrides.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "kyc.updated"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "subAccountId": {
                        "type": "string"
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "kycStatus": {
                        "type": "string",
                        "enum": [
                          "NONE",
                          "PENDING",
                          "VERIFIED",
                          "REJECTED"
                        ]
                      },
                      "kycLevel": {
                        "type": "string",
                        "enum": [
                          "NONE",
                          "BASIC",
                          "FULL"
                        ]
                      },
                      "reference": {
                        "type": "string",
                        "nullable": true,
                        "description": "Partner-supplied KYC reference, if provided."
                      },
                      "reasonCode": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Stable rejection code. Present when kycStatus is REJECTED."
                      },
                      "reason": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Human-readable remediation guidance. Present when kycStatus is REJECTED."
                      },
                      "reviewedAt": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time",
                        "description": "Decision timestamp. Present when kycStatus is REJECTED."
                      },
                      "overriddenByAdmin": {
                        "type": "boolean",
                        "description": "Present and true when overridden by MyStocks compliance."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "account.frozen": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Sub-account frozen or unfrozen",
        "description": "Fired on both freeze and unfreeze of a sub-account, and when a partner key is revoked.\nUse the `frozen` boolean to determine direction. Key-revocation payloads omit\n`subAccountId`/`externalId` and include `revokedBy` and `reason` instead.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "account.frozen"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "subAccountId": {
                        "type": "string",
                        "description": "Present for sub-account events. Omitted for key-revocation events."
                      },
                      "externalId": {
                        "type": "string",
                        "nullable": true
                      },
                      "frozen": {
                        "type": "boolean"
                      },
                      "frozenBy": {
                        "type": "string",
                        "example": "platform_admin",
                        "description": "Present when frozen."
                      },
                      "unfrozenBy": {
                        "type": "string",
                        "example": "platform_admin",
                        "description": "Present when unfrozen."
                      },
                      "reason": {
                        "type": "string",
                        "description": "Key-revocation only."
                      },
                      "revokedBy": {
                        "type": "string",
                        "description": "Key-revocation only."
                      },
                      "revokedAt": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Key-revocation only."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "dividend.paid": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Dividend distributed to sub-accounts",
        "description": "Fired once per dividend distribution batch, grouped by symbol.\nThe `distributions` array lists every sub-account that received a payout.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "dividend.paid"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "symbol": {
                        "type": "string",
                        "example": "SCOM.KE"
                      },
                      "name": {
                        "type": "string"
                      },
                      "dividendPerShare": {
                        "type": "number"
                      },
                      "currency": {
                        "type": "string",
                        "example": "KES"
                      },
                      "totalUsdPaid": {
                        "type": "number"
                      },
                      "distributionCount": {
                        "type": "integer"
                      },
                      "distributions": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "subAccountId": {
                              "type": "string"
                            },
                            "externalId": {
                              "type": "string",
                              "nullable": true
                            },
                            "units": {
                              "type": "number"
                            },
                            "usdYield": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "corporateaction.declared": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Corporate action declared on a held symbol",
        "description": "Fired when a corporate action (split, rights issue, bonus, merger, delisting, symbol change, …)\nis declared for a symbol that at least one of your sub-accounts holds — so you can adjust\ncached positions and notify your users. Sent once per corporate action per partner;\n`affectedSubAccounts` lists your holders of the symbol.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "corporateaction.declared"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "corporateActionId": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string",
                        "nullable": true,
                        "example": "SPLIT"
                      },
                      "symbol": {
                        "type": "string",
                        "example": "SCOM.KE"
                      },
                      "exchange": {
                        "type": "string",
                        "nullable": true
                      },
                      "newSymbol": {
                        "type": "string",
                        "nullable": true
                      },
                      "oldSymbol": {
                        "type": "string",
                        "nullable": true
                      },
                      "title": {
                        "type": "string",
                        "nullable": true
                      },
                      "description": {
                        "type": "string",
                        "nullable": true
                      },
                      "exDate": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "recordDate": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "payDate": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "effectiveDate": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "amount": {
                        "type": "number",
                        "nullable": true
                      },
                      "ratio": {
                        "type": "string",
                        "nullable": true
                      },
                      "affectedSubAccounts": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "subAccountId": {
                              "type": "string"
                            },
                            "externalId": {
                              "type": "string",
                              "nullable": true
                            },
                            "units": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "incident.declared": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Platform incident declared",
        "description": "Broadcast to all active partners when MyStocks declares an incident. Severity levels P0 (critical) to P3 (minor).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "incident.declared"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      },
                      "severity": {
                        "type": "string",
                        "enum": [
                          "P0",
                          "P1",
                          "P2",
                          "P3"
                        ]
                      },
                      "affectedServices": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "status": {
                        "type": "string",
                        "example": "investigating"
                      },
                      "startedAt": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "incident.resolved": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Platform incident resolved",
        "description": "Broadcast to all active partners when a declared incident is closed.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "incident.resolved"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "title": {
                        "type": "string"
                      },
                      "severity": {
                        "type": "string",
                        "enum": [
                          "P0",
                          "P1",
                          "P2",
                          "P3"
                        ]
                      },
                      "status": {
                        "type": "string",
                        "example": "resolved"
                      },
                      "resolvedAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "duration": {
                        "type": "string",
                        "example": "2h 30m",
                        "description": "Human-readable resolution time."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "quote.expired": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Pre-trade quote expired unused",
        "description": "Fired when a pre-trade quote (`GET /quote/{symbol}`) reaches its TTL\n(`quoteTtlSeconds`, currently 60s) without being used to place an order —\nso your app can clear a stale price preview and prompt the user to\nrefresh. Quotes consumed by a trade are marked used and never emit this\nevent. Delivered up to ~60s after the exact `expiresAt` (the sweep runs\nevery minute); the client already has `quoteExpiresAt` for the precise\nmoment. `subAccountId` is present only for sub-account quotes.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "quote.expired"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "quoteId": {
                        "type": "string",
                        "example": "qt_9f3c1a2b4d5e6f708192a3b4c5d6e7f8"
                      },
                      "symbol": {
                        "type": "string",
                        "example": "SCOM.KE"
                      },
                      "exchange": {
                        "type": "string",
                        "nullable": true,
                        "example": "NSE"
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "BUY",
                          "SELL"
                        ]
                      },
                      "quantity": {
                        "type": "number",
                        "nullable": true
                      },
                      "cashValue": {
                        "type": "number",
                        "nullable": true
                      },
                      "subAccountId": {
                        "type": "string",
                        "nullable": true
                      },
                      "issuedAt": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "expiresAt": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "market.status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/WebhookSignature"
        },
        {
          "$ref": "#/components/parameters/WebhookEvent"
        },
        {
          "$ref": "#/components/parameters/WebhookAttempt"
        }
      ],
      "post": {
        "summary": "Exchange trading session changed (open/close/holiday)",
        "description": "Broadcast to all active partners whenever an exchange's session phase\nchanges — CLOSED→OPEN at the opening bell, OPEN→CLOSED at the close, or a\ntransition into/out of a HOLIDAY. Edge-triggered: exactly one event per\ntransition, not a heartbeat. Drive a \"market open/closed\" badge or gate\norder entry in your UI. `previousStatus` is the phase before the change;\n`nextOpen` is the next session start when currently closed.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "example": "market.status"
                  },
                  "timestamp": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "exchange": {
                        "type": "string",
                        "example": "NSE"
                      },
                      "mic": {
                        "type": "string",
                        "description": "ISO 10383 operating MIC.",
                        "example": "XNAI"
                      },
                      "name": {
                        "type": "string",
                        "example": "Nairobi Securities Exchange"
                      },
                      "country": {
                        "type": "string",
                        "example": "Kenya"
                      },
                      "currency": {
                        "type": "string",
                        "example": "KES"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "OPEN",
                          "CLOSED",
                          "HOLIDAY"
                        ]
                      },
                      "previousStatus": {
                        "type": "string",
                        "enum": [
                          "OPEN",
                          "CLOSED",
                          "HOLIDAY"
                        ]
                      },
                      "isOpen": {
                        "type": "boolean"
                      },
                      "localOpen": {
                        "type": "string",
                        "example": "09:00"
                      },
                      "localClose": {
                        "type": "string",
                        "example": "15:00"
                      },
                      "timezone": {
                        "type": "string",
                        "example": "Africa/Nairobi"
                      },
                      "nextOpen": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                      },
                      "holiday": {
                        "type": "string",
                        "nullable": true,
                        "description": "Present only when status is HOLIDAY."
                      },
                      "changedAt": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer sk_sandbox_KEY (sandbox) or pk_live_KEY (production)"
      },
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Alternative to the Authorization header. Same key value."
      },
      "FirebaseToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Firebase user ID token (`Authorization: Bearer <firebase-id-token>`), NOT a partner API key. Used by the production partner-portal endpoints /register, /session, /me, /upgrade-request, and /api-keys/revoke. Obtain it by signing in at the partner dashboard. Sandbox counterparts may use sandbox-key authentication instead."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Unique string that makes write calls safe to retry on network failure.\nRequired for money movement, trading, KYC, subscriptions, and webhook creation.\nDeduplicated for 24 hours -- duplicate keys return the cached response.\nHTTP 409 if a concurrent request with the same key is still in progress.\n",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 200,
          "example": "dep_user42_1743152580"
        }
      },
      "WebhookSignature": {
        "name": "x-mystocks-signature",
        "in": "header",
        "required": true,
        "description": "HMAC-SHA256 of the raw request body using your webhook signing secret, hex-encoded and prefixed with `sha256=`. Verify with a constant-time comparison before trusting the payload.",
        "schema": {
          "type": "string",
          "example": "sha256=3f1d9c…"
        }
      },
      "WebhookEvent": {
        "name": "x-mystocks-event",
        "in": "header",
        "required": true,
        "description": "Event type of this delivery. Matches the `event` field in the payload.",
        "schema": {
          "type": "string",
          "example": "order.filled"
        }
      },
      "WebhookAttempt": {
        "name": "x-mystocks-attempt",
        "in": "header",
        "required": true,
        "description": "Delivery attempt number, starting at 1. Failed deliveries are retried with exponential back-off — up to 6 attempts total (immediate, then 5 s, 30 s, 5 min, 30 min, 2 h).",
        "schema": {
          "type": "string",
          "example": "1"
        }
      }
    },
    "headers": {
      "XRateLimitLimit": {
        "description": "Request quota per minute for your tier. Present on every authenticated sandbox and production response.",
        "schema": {
          "type": "integer",
          "example": 500
        }
      },
      "XRateLimitRemaining": {
        "description": "Requests remaining in the current per-minute sliding window. Present on every authenticated sandbox and production response.",
        "schema": {
          "type": "integer",
          "example": 497
        }
      },
      "XRateLimitReset": {
        "description": "Unix epoch seconds when the current window resets. Present on every authenticated sandbox and production response.",
        "schema": {
          "type": "integer",
          "example": 1743152640
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying. `0` on successful responses; positive on `429`.",
        "schema": {
          "type": "integer",
          "example": 30
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable application sub-error code.",
                "example": "INSUFFICIENT_FUNDS"
              },
              "message": {
                "type": "string",
                "description": "Human-readable error message.",
                "example": "Insufficient funds. Required: $9250.00, Available: $3200.00"
              }
            }
          }
        }
      },
      "QuoteDataQuality": {
        "type": "object",
        "description": "The published market-data freshness contract. African equity quotes are supplied by the exchanges on a 15-minute-delayed basis, not in real time. MyStocks holds full exchange data rights. The API refresh target is 900 seconds during the published coverage window, with an end-of-day pass after market close.\n",
        "required": [
          "asOf",
          "ageSeconds",
          "stale",
          "delayMode",
          "delayMinutes",
          "refreshIntervalSeconds",
          "provider",
          "disclaimer"
        ],
        "properties": {
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Timestamp of the exchange observation, not the time the API response was generated."
          },
          "ageSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Age of the price in seconds (now − asOf)."
          },
          "stale": {
            "type": "boolean",
            "description": "True when the price is older than two refresh intervals."
          },
          "delayMode": {
            "type": "string",
            "enum": [
              "DELAYED",
              "END_OF_DAY"
            ],
            "description": "DELAYED — exchange-supplied data in the 15-minute-delayed latency class. END_OF_DAY — outside the window; this is the most recent close, not a live quote.\n"
          },
          "delayMinutes": {
            "type": "integer",
            "example": 15,
            "description": "Exchange feed latency class in minutes. This is distinct from the API refresh target."
          },
          "refreshIntervalSeconds": {
            "type": "integer",
            "example": 900,
            "description": "Target interval between API ingestion refreshes during the published coverage window."
          },
          "provider": {
            "type": "string",
            "enum": [
              "mstocks",
              "mansa"
            ],
            "description": "MyStocks ingestion connector for this exchange feed. This field does not describe exchange licensing or partner entitlement."
          },
          "disclaimer": {
            "type": "string",
            "description": "Plain-English statement of what this price is and is not."
          }
        }
      },
      "PriceAlert": {
        "type": "object",
        "description": "A registered price threshold. Crossing it while the exchange is open fires the `price.alert` webhook.",
        "properties": {
          "alertId": {
            "type": "string",
            "example": "alr_abc123"
          },
          "clientAlertId": {
            "type": "string",
            "nullable": true,
            "description": "Your own reference, supplied at creation."
          },
          "symbol": {
            "type": "string",
            "example": "SCOM.KE"
          },
          "exchange": {
            "type": "string",
            "example": "NSE"
          },
          "condition": {
            "type": "string",
            "enum": [
              "above",
              "below"
            ]
          },
          "threshold": {
            "type": "number",
            "description": "In the instrument's local trading currency.",
            "example": 20
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "description": "The local trading currency the threshold is denominated in.",
            "example": "KES"
          },
          "repeat": {
            "type": "boolean"
          },
          "state": {
            "type": "string",
            "enum": [
              "ARMED",
              "TRIGGERED",
              "DISARMED"
            ],
            "description": "ARMED — waiting for the threshold to be crossed.\nTRIGGERED — a repeat alert that has fired and is waiting for the price to cross back before re-arming.\nDISARMED — a one-shot alert that has fired and is spent.\n"
          },
          "lastPriceLocal": {
            "type": "number",
            "nullable": true
          },
          "triggerCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastTriggeredAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PartnerPilotPolicy": {
        "type": "object",
        "description": "Controlled-production policy. When enabled, production mutations fail closed unless admission, ownership, feature, limit, and reconciliation controls pass.",
        "required": [
          "enabled",
          "status",
          "manualTreasuryReview",
          "features",
          "limits",
          "reconciliation",
          "readiness"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "NOT_APPROVED",
              "APPROVED",
              "PAUSED",
              "COMPLETED"
            ]
          },
          "operationalOwner": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/PilotContact"
              }
            ]
          },
          "incidentContact": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/PilotContact"
              }
            ]
          },
          "manualTreasuryReview": {
            "type": "boolean",
            "description": "Always true for a controlled pilot; master-wallet top-ups and payouts remain pending until MyStocks treasury review."
          },
          "features": {
            "type": "object",
            "required": [
              "customerOnboarding",
              "funding",
              "trading",
              "withdrawals"
            ],
            "properties": {
              "customerOnboarding": {
                "type": "boolean"
              },
              "funding": {
                "type": "boolean"
              },
              "trading": {
                "type": "boolean"
              },
              "withdrawals": {
                "type": "boolean"
              }
            }
          },
          "limits": {
            "type": "object",
            "required": [
              "maxCustomers",
              "maxTransactionUsd",
              "maxDailyCustomerUsd",
              "maxDailyPartnerUsd"
            ],
            "properties": {
              "maxCustomers": {
                "type": "integer",
                "minimum": 1
              },
              "maxTransactionUsd": {
                "type": "number",
                "exclusiveMinimum": 0
              },
              "maxDailyCustomerUsd": {
                "type": "number",
                "exclusiveMinimum": 0
              },
              "maxDailyPartnerUsd": {
                "type": "number",
                "exclusiveMinimum": 0
              }
            }
          },
          "reconciliation": {
            "type": "object",
            "required": [
              "required",
              "maxAgeHours",
              "blockOnExceptions"
            ],
            "properties": {
              "required": {
                "type": "boolean"
              },
              "maxAgeHours": {
                "type": "number",
                "minimum": 24,
                "maximum": 48
              },
              "blockOnExceptions": {
                "type": "boolean"
              },
              "lastCompletedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "lastStatus": {
                "type": "string",
                "enum": [
                  "BALANCED",
                  "EXCEPTIONS",
                  "INCOMPLETE"
                ],
                "nullable": true
              },
              "evidenceUrl": {
                "type": "string",
                "format": "uri",
                "nullable": true
              }
            }
          },
          "readiness": {
            "type": "object",
            "description": "Evidence and approvals required before mutations may resume and before the controlled pilot may complete.",
            "required": [
              "rollbackPlanUrl",
              "eventDeliveryVerifiedAt",
              "failoverTestedAt",
              "approvals",
              "exitEvidenceUrl"
            ],
            "properties": {
              "rollbackPlanUrl": {
                "type": "string",
                "format": "uri",
                "nullable": true
              },
              "eventDeliveryVerifiedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "failoverTestedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "approvals": {
                "type": "object",
                "required": [
                  "operations",
                  "risk",
                  "compliance",
                  "security"
                ],
                "properties": {
                  "operations": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "risk": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "compliance": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "security": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                }
              },
              "exitEvidenceUrl": {
                "type": "string",
                "format": "uri",
                "nullable": true
              }
            }
          }
        }
      },
      "PilotContact": {
        "type": "object",
        "required": [
          "name",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": [
          "businessName",
          "email"
        ],
        "properties": {
          "businessName": {
            "type": "string",
            "minLength": 2,
            "description": "Your company or project name.",
            "example": "Acme Corp"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Contact email. Used to prevent duplicate registrations.",
            "example": "dev@acme.com"
          }
        }
      },
      "AutoRegisterRequest": {
        "type": "object",
        "required": [
          "uid"
        ],
        "properties": {
          "uid": {
            "type": "string",
            "description": "Your internal user identifier. Idempotency key -- calling twice with the same uid returns the same sub-account.",
            "example": "user_42"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address for the sub-account user.",
            "example": "alice@yourapp.com"
          },
          "name": {
            "type": "string",
            "description": "Full name shown in the MyStocks admin console.",
            "example": "Alice K."
          },
          "phone": {
            "type": "string",
            "description": "User phone number in E.164 format.",
            "example": "+254712345678"
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country code.",
            "example": "KE"
          }
        }
      },
      "CreateSubAccountRequest": {
        "type": "object",
        "required": [
          "externalId"
        ],
        "properties": {
          "externalId": {
            "type": "string",
            "description": "Your internal user identifier. Must be unique per partner.",
            "example": "usr_8821"
          },
          "displayName": {
            "type": "string",
            "description": "Full name shown in the MyStocks admin console.",
            "example": "Alice K."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address for the sub-account user.",
            "example": "alice@yourapp.com"
          },
          "phone": {
            "type": "string",
            "description": "Customer phone number in international format when available.",
            "example": "+254712345678"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code used for onboarding and compliance context.",
            "example": "KE"
          }
        }
      },
      "UpdateSubAccountRequest": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string",
            "description": "New display name for the sub-account.",
            "example": "Alice Kamau"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "New email address."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "frozen"
            ],
            "description": "frozen suspends all trading, deposits and withdrawals. Set back to active to restore access."
          }
        }
      },
      "CloseSubAccountRequest": {
        "type": "object",
        "properties": {
          "residualCashHandling": {
            "type": "string",
            "enum": [
              "require_zero",
              "transfer_to_partner"
            ],
            "default": "require_zero",
            "description": "How to handle a remaining USD wallet balance. `require_zero` blocks closure if cash remains. `transfer_to_partner` atomically moves residual cash to the partner master wallet before closing."
          },
          "reason": {
            "type": "string",
            "maxLength": 500,
            "description": "Partner-supplied closure reason stored for audit and support.",
            "example": "User requested account deletion in partner app."
          }
        }
      },
      "DepositRequest": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "USD ledger amount to deposit, unless `currency` is a non-USD ISO code. If currency is non-USD, amount is interpreted in that currency and converted to USD by MyStocks managed FX.",
            "example": 500
          },
          "amountUsd": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Explicit USD ledger amount. Use this when you also include local display fields.",
            "example": 500
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "description": "Currency of `amount`. Defaults to USD. Non-USD amounts are converted with MyStocks managed FX.",
            "example": "KES"
          },
          "note": {
            "type": "string",
            "description": "Optional description shown in the transaction history.",
            "example": "Mpesa STK push ref KE2482"
          },
          "localAmount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Equivalent amount in the user local currency. Stored for audit and compliance only.",
            "example": 65000
          },
          "localCurrency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "description": "ISO 4217 code for the local amount.",
            "example": "KES"
          },
          "fxRate": {
            "type": "number",
            "exclusiveMinimum": 0,
            "deprecated": true,
            "description": "Legacy partner-provided exchange rate. Omit this to use MyStocks managed FX.",
            "example": 130
          }
        }
      },
      "WithdrawRequest": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "USD ledger amount to move, unless `currency` is a non-USD ISO code. If currency is non-USD, amount is interpreted in that currency and converted to USD by MyStocks managed FX.",
            "example": 200
          },
          "amountUsd": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Explicit USD ledger amount. Use this when you also include local cash-out display fields.",
            "example": 200
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "description": "Currency of `amount`. Defaults to USD. Non-USD amounts are converted with MyStocks managed FX.",
            "example": "KES"
          },
          "note": {
            "type": "string",
            "description": "Optional description shown in transaction history."
          },
          "localAmount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Equivalent in local currency for the audit trail.",
            "example": 26000
          },
          "localCurrency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "description": "ISO 4217 code.",
            "example": "KES"
          },
          "fxRate": {
            "type": "number",
            "exclusiveMinimum": 0,
            "deprecated": true,
            "description": "Legacy partner-provided exchange rate. Omit this to use MyStocks managed FX.",
            "example": 130
          }
        }
      },
      "TradeRequest": {
        "type": "object",
        "required": [
          "symbol",
          "type"
        ],
        "oneOf": [
          {
            "required": [
              "quantity"
            ],
            "not": {
              "required": [
                "cashValue"
              ]
            },
            "title": "Share-sized order"
          },
          {
            "required": [
              "cashValue"
            ],
            "not": {
              "required": [
                "quantity"
              ]
            },
            "title": "Cash-value fractional order"
          }
        ],
        "allOf": [
          {
            "if": {
              "anyOf": [
                {
                  "not": {
                    "required": [
                      "orderType"
                    ]
                  }
                },
                {
                  "properties": {
                    "orderType": {
                      "const": "MARKET"
                    }
                  }
                }
              ]
            },
            "then": {
              "required": [
                "quoteId"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "orderType": {
                  "const": "LIMIT"
                }
              },
              "required": [
                "orderType"
              ]
            },
            "then": {
              "required": [
                "limitPrice"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "orderType": {
                  "const": "STOP"
                }
              },
              "required": [
                "orderType"
              ]
            },
            "then": {
              "required": [
                "stopPrice"
              ]
            }
          },
          {
            "if": {
              "properties": {
                "orderType": {
                  "const": "STOP_LIMIT"
                }
              },
              "required": [
                "orderType"
              ]
            },
            "then": {
              "required": [
                "limitPrice",
                "stopPrice"
              ]
            }
          }
        ],
        "properties": {
          "symbol": {
            "type": "string",
            "description": "Stock symbol. Accepts exchange-qualified (SCOM.KE, DANGCEM.NG) or bare ticker (SCOM) -- auto-resolved when unambiguous. Returns 422 with match list if ambiguous.",
            "example": "SCOM.KE"
          },
          "type": {
            "type": "string",
            "enum": [
              "BUY",
              "SELL"
            ],
            "description": "Order direction.",
            "example": "BUY"
          },
          "quantity": {
            "type": "number",
            "exclusiveMinimum": 0,
            "multipleOf": 0.000001,
            "description": "Number of shares. Whole-share markets require integer quantities; fractional-enabled markets allow up to 6 decimal places. Mutually exclusive with cashValue.",
            "example": 500
          },
          "cashValue": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "USD notional amount for cash-mode fractional investing. Mutually exclusive with quantity and must match the cashValue used to obtain quoteId for MARKET orders.",
            "example": 50
          },
          "quoteId": {
            "type": "string",
            "description": "Fresh pre-trade quote ID from GET /quote/{symbol}. Quotes expire after 60 seconds and can be used once. Required for MARKET orders, including the default when orderType is omitted; ignored for LIMIT/STOP/STOP_LIMIT.\n",
            "example": "qt_2f4d5a7b8c9e4f01a2b3c4d5e6f78901"
          },
          "orderType": {
            "type": "string",
            "enum": [
              "MARKET",
              "LIMIT",
              "STOP",
              "STOP_LIMIT"
            ],
            "default": "MARKET",
            "description": "MARKET (default): quote-gated and routed to automatic MyStocks internal-book execution. LIMIT/STOP/STOP_LIMIT are resting orders — they are placed WITHOUT a quote, escrow at the trigger price, rest in status WORKING, and route to automatic internal-book execution when the latest delayed price observation crosses the trigger (during market hours). BUY LIMIT fills at/below limitPrice; SELL LIMIT at/above; BUY STOP triggers at/above stopPrice; SELL STOP at/below. Prices are in the stock's LOCAL trading currency. Fires order.triggered on activation.\n"
          },
          "limitPrice": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Local-currency limit price. Required for LIMIT and STOP_LIMIT."
          },
          "stopPrice": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Local-currency stop/trigger price. Required for STOP and STOP_LIMIT."
          },
          "clientOrderId": {
            "type": "string",
            "maxLength": 80,
            "description": "Optional partner-supplied order reference. Must be unique per partner when supplied.",
            "example": "partner-ord-10001"
          },
          "timeInForce": {
            "type": "string",
            "enum": [
              "DAY",
              "GTC",
              "GTD"
            ],
            "default": "GTC",
            "description": "Order validity instruction, enforced by the OMS lifecycle daemon. GTC (default) keeps the order live until executed or cancelled. DAY orders auto-cancel if not executed within 24 hours; GTD orders auto-cancel at expiresAt. On expiry, BUY escrow is refunded, SELL reservations are released, and an order.cancelled webhook fires. IOC is not supported by the current MyStocks internal-book order contract.\n"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Required for GTD orders. Stored for OMS expiry governance."
          },
          "orderInstruction": {
            "type": "string",
            "maxLength": 500,
            "description": "Optional free-text execution instruction for the MyStocks internal-book engine."
          },
          "stopLoss": {
            "type": "number",
            "description": "Optional. Order auto-cancels if the stock falls below this USD price before settlement.",
            "example": 0.11
          },
          "takeProfit": {
            "type": "number",
            "description": "Optional. Order auto-settles if the stock rises above this USD price before settlement.",
            "example": 0.16
          }
        }
      },
      "KycRequest": {
        "type": "object",
        "required": [
          "status",
          "level"
        ],
        "allOf": [
          {
            "if": {
              "properties": {
                "status": {
                  "const": "REJECTED"
                }
              },
              "required": [
                "status"
              ]
            },
            "then": {
              "required": [
                "reasonCode",
                "reason"
              ],
              "properties": {
                "level": {
                  "const": "NONE"
                }
              }
            }
          }
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "NONE",
              "PENDING",
              "VERIFIED",
              "REJECTED"
            ],
            "description": "Overall KYC status of the user.",
            "example": "VERIFIED"
          },
          "level": {
            "type": "string",
            "enum": [
              "NONE",
              "BASIC",
              "FULL"
            ],
            "description": "BASIC = government ID verified (sufficient for trading). FULL = address + enhanced due diligence.",
            "example": "BASIC"
          },
          "provider": {
            "type": "string",
            "description": "Name of the KYC provider that performed the verification (e.g. sumsub, smile_identity).",
            "example": "sumsub"
          },
          "reference": {
            "type": "string",
            "description": "Your internal KYC session reference for audit correlation.",
            "example": "kyc_session_88721"
          },
          "reasonCode": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64,
            "pattern": "^[A-Z0-9_]+$",
            "description": "Required when status is REJECTED. Stable machine-readable rejection code.",
            "example": "DOCUMENT_EXPIRED"
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Required when status is REJECTED. Human-readable remediation guidance safe to show to the customer.",
            "example": "Submit a current government-issued identity document."
          },
          "idDocumentType": {
            "type": "string",
            "enum": [
              "PASSPORT",
              "NATIONAL_ID",
              "DRIVERS_LICENSE",
              "VOTERS_CARD",
              "RESIDENCE_PERMIT",
              "OTHER"
            ],
            "description": "Type of identity document verified during your KYC process.",
            "example": "NATIONAL_ID"
          },
          "idNumber": {
            "type": "string",
            "description": "Identity document number. Stored as a sha256 fingerprint plus the last 4 characters only — never persisted or returned in full. The response and webhook echo back idNumberLast4.",
            "example": "24681012"
          },
          "dateOfBirth": {
            "type": "string",
            "format": "date",
            "description": "Date of birth in ISO YYYY-MM-DD format.",
            "example": "1990-04-17"
          },
          "nationality": {
            "type": "string",
            "description": "Nationality as an ISO 3166-1 alpha-2 country code.",
            "example": "KE"
          },
          "countryOfResidence": {
            "type": "string",
            "description": "Country of residence as an ISO 3166-1 alpha-2 country code.",
            "example": "KE"
          },
          "address": {
            "type": "string",
            "maxLength": 500,
            "description": "Residential address (free-form, up to 500 characters).",
            "example": "12 Kenyatta Ave, Nairobi"
          },
          "taxResidency": {
            "type": "string",
            "description": "Country of tax residency as an ISO 3166-1 alpha-2 country code.",
            "example": "KE"
          },
          "taxId": {
            "type": "string",
            "description": "Taxpayer identification number (TIN). Stored as a sha256 fingerprint plus the last 4 characters only — never persisted or returned in full. The response and webhook echo back taxIdLast4.",
            "example": "A012345678Z"
          },
          "pep": {
            "type": "boolean",
            "description": "Whether the user is a politically exposed person.",
            "example": false
          },
          "sanctionsResult": {
            "type": "string",
            "enum": [
              "CLEAR",
              "FLAGGED",
              "PENDING"
            ],
            "description": "Result of your sanctions/PEP screening.",
            "example": "CLEAR"
          },
          "screeningProvider": {
            "type": "string",
            "description": "Name of the sanctions/PEP screening provider.",
            "example": "comply_advantage"
          },
          "riskRating": {
            "type": "string",
            "enum": [
              "LOW",
              "MEDIUM",
              "HIGH"
            ],
            "description": "Risk rating assigned to the user by your onboarding process.",
            "example": "LOW"
          },
          "consentAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp when the user granted consent for processing.",
            "example": "2026-07-11T09:30:00Z"
          },
          "evidenceReference": {
            "type": "string",
            "description": "Your reference/URL to the stored KYC evidence bundle for audit.",
            "example": "kyc_docs_88721"
          },
          "employmentStatus": {
            "type": "string",
            "enum": [
              "EMPLOYED",
              "SELF_EMPLOYED",
              "UNEMPLOYED",
              "STUDENT",
              "RETIRED",
              "OTHER"
            ],
            "description": "Employment status captured during onboarding.",
            "example": "EMPLOYED"
          },
          "occupation": {
            "type": "string",
            "description": "User occupation or role.",
            "example": "Software engineer"
          },
          "employerName": {
            "type": "string",
            "description": "Employer or business name when available.",
            "example": "Acme Ltd"
          },
          "sourceOfFunds": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "SALARY",
                  "BUSINESS_INCOME",
                  "INVESTMENTS",
                  "INHERITANCE",
                  "SAVINGS",
                  "PENSION",
                  "GIFT",
                  "OTHER"
                ]
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "SALARY",
                    "BUSINESS_INCOME",
                    "INVESTMENTS",
                    "INHERITANCE",
                    "SAVINGS",
                    "PENSION",
                    "GIFT",
                    "OTHER"
                  ]
                }
              }
            ],
            "description": "Source of funds used for investing.",
            "example": "SALARY"
          },
          "sourceOfWealth": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "BUSINESS_OWNERSHIP",
                  "EMPLOYMENT_INCOME",
                  "INVESTMENTS",
                  "INHERITANCE",
                  "PROPERTY",
                  "SAVINGS",
                  "PENSION",
                  "OTHER"
                ]
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "BUSINESS_OWNERSHIP",
                    "EMPLOYMENT_INCOME",
                    "INVESTMENTS",
                    "INHERITANCE",
                    "PROPERTY",
                    "SAVINGS",
                    "PENSION",
                    "OTHER"
                  ]
                }
              }
            ],
            "description": "Source of accumulated wealth.",
            "example": "EMPLOYMENT_INCOME"
          },
          "annualIncomeBand": {
            "type": "string",
            "enum": [
              "UNDER_10000",
              "10000_50000",
              "50000_100000",
              "100000_250000",
              "250000_1000000",
              "OVER_1000000"
            ],
            "description": "Annual income band in USD-equivalent.",
            "example": "50000_100000"
          },
          "netWorthBand": {
            "type": "string",
            "enum": [
              "UNDER_25000",
              "25000_100000",
              "100000_500000",
              "500000_1000000",
              "1000000_5000000",
              "OVER_5000000"
            ],
            "description": "Net-worth band in USD-equivalent.",
            "example": "100000_500000"
          },
          "investmentExperience": {
            "type": "string",
            "enum": [
              "NONE",
              "LIMITED",
              "MODERATE",
              "EXPERIENCED",
              "PROFESSIONAL"
            ],
            "description": "User's investing experience level.",
            "example": "LIMITED"
          },
          "investmentObjectives": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "CAPITAL_PRESERVATION",
                "INCOME",
                "GROWTH",
                "SPECULATION",
                "HEDGING",
                "RETIREMENT",
                "OTHER"
              ]
            },
            "description": "Investment objectives captured during suitability/appropriateness checks.",
            "example": [
              "GROWTH",
              "RETIREMENT"
            ]
          },
          "riskTolerance": {
            "type": "string",
            "enum": [
              "LOW",
              "MEDIUM",
              "HIGH"
            ],
            "description": "User-declared or assessed risk tolerance.",
            "example": "MEDIUM"
          },
          "suitabilityStatus": {
            "type": "string",
            "enum": [
              "NOT_ASSESSED",
              "SUITABLE",
              "UNSUITABLE",
              "REVIEW_REQUIRED"
            ],
            "description": "Suitability outcome assigned by your onboarding process.",
            "example": "SUITABLE"
          },
          "appropriatenessStatus": {
            "type": "string",
            "enum": [
              "NOT_ASSESSED",
              "SUITABLE",
              "UNSUITABLE",
              "REVIEW_REQUIRED"
            ],
            "description": "Appropriateness outcome for execution-only brokerage products.",
            "example": "SUITABLE"
          },
          "assessmentReference": {
            "type": "string",
            "description": "Your suitability/appropriateness assessment reference.",
            "example": "suitability_88721"
          },
          "usPerson": {
            "type": "boolean",
            "description": "FATCA US-person declaration.",
            "example": false
          },
          "fatcaStatus": {
            "type": "string",
            "enum": [
              "NOT_US_PERSON",
              "US_PERSON",
              "EXEMPT",
              "UNKNOWN"
            ],
            "description": "FATCA classification asserted by your onboarding flow.",
            "example": "NOT_US_PERSON"
          },
          "crsTaxResidencies": {
            "type": "array",
            "description": "CRS tax residencies. Tax IDs are stored as sha256 fingerprint plus last4 only.",
            "items": {
              "type": "object",
              "required": [
                "country"
              ],
              "properties": {
                "country": {
                  "type": "string",
                  "example": "KE"
                },
                "taxId": {
                  "type": "string",
                  "description": "Stored as fingerprint + last4 only.",
                  "example": "A012345678Z"
                },
                "reasonNoTin": {
                  "type": "string",
                  "description": "Reason no TIN was supplied, if applicable."
                }
              }
            }
          },
          "marketAccountReferences": {
            "type": "array",
            "description": "Market-specific brokerage/CSD account references. Account numbers are stored as fingerprint plus last4 only.",
            "items": {
              "type": "object",
              "required": [
                "market"
              ],
              "properties": {
                "market": {
                  "type": "string",
                  "example": "NSE"
                },
                "broker": {
                  "type": "string",
                  "example": "MyStocks Securities"
                },
                "brokerAccountNumber": {
                  "type": "string",
                  "description": "Stored as fingerprint + last4 only."
                },
                "csdNumber": {
                  "type": "string",
                  "description": "Stored as fingerprint + last4 only."
                }
              }
            }
          }
        }
      },
      "DeviceRegistrationRequest": {
        "type": "object",
        "required": [
          "token",
          "platform",
          "provider"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "Mobile/web push token from APNs, FCM, Expo, or Web Push. Stored as sha256 hash plus last4 only.",
            "example": "ExpoPushToken[xxxxxxxxxxxxxxxxxxxxxx]"
          },
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "web"
            ],
            "example": "ios"
          },
          "provider": {
            "type": "string",
            "enum": [
              "fcm",
              "apns",
              "expo",
              "webpush"
            ],
            "example": "expo"
          },
          "appId": {
            "type": "string",
            "description": "Partner app bundle/package ID.",
            "example": "com.afritrade.app"
          },
          "deviceId": {
            "type": "string",
            "description": "Optional partner device identifier. Used to keep one stable MyStocks deviceId across token rotations.",
            "example": "afritrade-device-42"
          },
          "locale": {
            "type": "string",
            "example": "en-KE"
          },
          "timezone": {
            "type": "string",
            "example": "Africa/Nairobi"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "retail",
              "beta"
            ]
          },
          "enabled": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "NotificationDevice": {
        "type": "object",
        "properties": {
          "deviceId": {
            "type": "string",
            "example": "dev_6f9a4d2c1b0e8a7c3d5f9012"
          },
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "web"
            ]
          },
          "provider": {
            "type": "string",
            "enum": [
              "fcm",
              "apns",
              "expo",
              "webpush"
            ]
          },
          "tokenLast4": {
            "type": "string",
            "nullable": true,
            "description": "Last 4 characters of the token for support correlation."
          },
          "appId": {
            "type": "string",
            "nullable": true
          },
          "locale": {
            "type": "string",
            "nullable": true
          },
          "timezone": {
            "type": "string",
            "nullable": true
          },
          "externalDeviceId": {
            "type": "string",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "revoked"
            ]
          },
          "createdAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "lastSeenAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          }
        }
      },
      "SubscribeRequest": {
        "type": "object",
        "required": [
          "assetType",
          "assetId"
        ],
        "properties": {
          "assetType": {
            "type": "string",
            "enum": [
              "BOND",
              "FUND",
              "OPPORTUNITY",
              "PRE_IPO"
            ],
            "description": "Asset class to subscribe to.",
            "example": "FUND"
          },
          "assetId": {
            "type": "string",
            "description": "Firestore document ID. Obtain from the list endpoints (/bonds, /funds, /opportunities).",
            "example": "fund_mmf_africa"
          },
          "units": {
            "type": "number",
            "description": "Whole units to subscribe. Required for BOND and FUND.",
            "example": 500
          },
          "amount": {
            "type": "number",
            "description": "USD commitment amount. Required for OPPORTUNITY and PRE_IPO.",
            "example": 1000
          }
        }
      },
      "RedeemRequest": {
        "type": "object",
        "required": [
          "holdingId",
          "unitsToRedeem"
        ],
        "properties": {
          "holdingId": {
            "type": "string",
            "description": "Sub-account fund holding doc ID (same as the fund Firestore doc ID).",
            "example": "fund_mmf_africa"
          },
          "unitsToRedeem": {
            "type": "number",
            "description": "Units to redeem. Must not exceed available unlocked units.",
            "example": 200
          }
        }
      },
      "WebhookCreateRequest": {
        "type": "object",
        "description": "Webhook signing secrets are encrypted at rest after registration and are never returned by list/get APIs.",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "HTTPS endpoint that receives POST requests. Must respond 2xx within 8 seconds.",
            "example": "https://yourapp.com/webhooks/mystocks"
          },
          "events": {
            "type": "array",
            "description": "Event types to subscribe to.",
            "items": {
              "type": "string",
              "enum": [
                "order.pending",
                "order.triggered",
                "order.filled",
                "order.rejected",
                "order.cancelled",
                "order.replaced",
                "trade.settled",
                "trade.rejected",
                "deposit.confirmed",
                "withdraw.confirmed",
                "wallet.credited",
                "topup.confirmed",
                "float.low",
                "kyc.updated",
                "account.frozen",
                "account.closed",
                "dividend.paid",
                "corporateaction.declared",
                "corporateaction.split",
                "corporateaction.suspension",
                "corporateaction.delisting",
                "corporateaction.rights_issue",
                "corporateaction.symbol_changed",
                "quote.expired",
                "market.status",
                "incident.declared",
                "incident.resolved"
              ]
            },
            "example": [
              "trade.settled",
              "deposit.confirmed",
              "kyc.updated"
            ]
          },
          "secret": {
            "type": "string",
            "minLength": 16,
            "description": "HMAC-SHA256 signing secret (min 16 chars). MyStocks sends x-mystocks-signature on every delivery.",
            "example": "my-signing-secret-min-16-chars"
          }
        }
      },
      "TopupRequest": {
        "type": "object",
        "required": [
          "amount",
          "paymentReference"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "USD amount to top up. Must be positive.",
            "example": 10000
          },
          "paymentReference": {
            "type": "string",
            "description": "Your wire transfer reference or payment ID. Unique per partner after normalization; duplicate references return 409.",
            "example": "WIRE-2026-05-001"
          },
          "paymentMethod": {
            "type": "string",
            "description": "Payment method, e.g. SWIFT wire, SEPA, ACH.",
            "example": "SWIFT wire"
          },
          "notes": {
            "type": "string",
            "description": "Optional note to the MyStocks operations team."
          }
        }
      },
      "PayoutRequest": {
        "type": "object",
        "required": [
          "amount",
          "bankDetails"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "USD to withdraw. Must not exceed master wallet balance.",
            "example": 5000
          },
          "bankDetails": {
            "type": "object",
            "required": [
              "bankName",
              "accountName",
              "accountNumber"
            ],
            "properties": {
              "bankName": {
                "type": "string",
                "description": "Name of the receiving bank.",
                "example": "Equity Bank Kenya"
              },
              "accountName": {
                "type": "string",
                "description": "Account holder name.",
                "example": "ACME Fintech Ltd"
              },
              "accountNumber": {
                "type": "string",
                "description": "Bank account number or IBAN.",
                "example": "0123456789"
              },
              "swiftCode": {
                "type": "string",
                "description": "SWIFT/BIC code for international wires.",
                "example": "EQBLKENA"
              }
            }
          },
          "notes": {
            "type": "string",
            "description": "Optional note to the MyStocks team."
          }
        }
      },
      "PartnerNotificationRule": {
        "type": "object",
        "required": [
          "event",
          "enabled",
          "channels",
          "recipients"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 120
          },
          "event": {
            "type": "string",
            "enum": [
              "float.low",
              "reconciliation.exception",
              "webhook.failed",
              "incident.opened",
              "approval.requested",
              "kyc.review",
              "usage.threshold",
              "certification.updated"
            ]
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "channels": {
            "type": "array",
            "minItems": 1,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "email",
                "webhook",
                "sms"
              ]
            }
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "uniqueItems": true,
            "items": {
              "type": "string"
            }
          },
          "severity": {
            "type": "string",
            "enum": [
              "all",
              "critical",
              "high"
            ],
            "default": "all"
          },
          "threshold": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "quietHours": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          }
        }
      },
      "ReconciliationSignoff": {
        "type": "object",
        "required": [
          "id",
          "partnerId",
          "from",
          "to",
          "status",
          "exceptionCount",
          "notes",
          "createdBy",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "partnerId": {
            "type": "string"
          },
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "status": {
            "type": "string",
            "maxLength": 30
          },
          "exceptionCount": {
            "type": "integer",
            "minimum": 0
          },
          "reportGeneratedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "notes": {
            "type": "string",
            "maxLength": 4000
          },
          "createdBy": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SettingsUpdateRequest": {
        "type": "object",
        "properties": {
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "HTTPS URL of your company logo. Shown in partner-branded emails.",
            "example": "https://cdn.yourapp.com/logo.png"
          },
          "emailConfig": {
            "type": [
              "object",
              "null"
            ],
            "description": "Custom SMTP config. Set to null to revert to MyStocks default emails.",
            "properties": {
              "host": {
                "type": "string",
                "description": "SMTP hostname. e.g. smtp.sendgrid.net, smtp.gmail.com.",
                "example": "smtp.sendgrid.net"
              },
              "port": {
                "type": "integer",
                "description": "SMTP port. Default 587 (STARTTLS). Use 465 for implicit TLS.",
                "example": 587
              },
              "secure": {
                "type": "boolean",
                "description": "true = implicit TLS (port 465). false = STARTTLS (port 587).",
                "example": false
              },
              "username": {
                "type": "string",
                "description": "SMTP auth username.",
                "example": "apikey"
              },
              "password": {
                "type": "string",
                "description": "SMTP password or API key. Omit to keep existing."
              },
              "fromName": {
                "type": "string",
                "description": "Display name in the From header.",
                "example": "Acme Investments"
              },
              "fromEmail": {
                "type": "string",
                "description": "From email address.",
                "example": "noreply@acme.com"
              }
            }
          }
        }
      },
      "EnterpriseSecurityPolicy": {
        "type": "object",
        "properties": {
          "ipAllowlist": {
            "type": "array",
            "description": "Exact IP addresses or IPv4 CIDR ranges allowed to call this partner key. Empty means no IP restriction.",
            "items": {
              "type": "string"
            },
            "example": [
              "203.0.113.10",
              "198.51.100.0/24"
            ]
          },
          "requireSignedRequests": {
            "type": "boolean",
            "description": "Require HMAC-signed non-GET requests. Send `X-MS-Timestamp` (ISO-8601, within a 5-minute window) and `X-MS-Signature` = hex `HMAC-SHA256(apiKey, \"{timestamp}.{METHOD}.{path}{?query}.{sha256(body)}\")`. The signed path includes the query string (leading `?`, empty when absent) so query-parameter tampering is detected. Signing without the query string is accepted for backward compatibility but deprecated.",
            "example": true
          },
          "requireMtls": {
            "type": "boolean",
            "description": "Require upstream mTLS verification headers from the API gateway.",
            "example": false
          },
          "scopedKeysRequired": {
            "type": "boolean",
            "description": "When true, API keys must carry explicit scopes — a key with no scopes is denied (403) rather than granted broad access. A key that should retain full access under this policy must hold the `*` scope. When false (default), unscoped keys keep broad access.",
            "example": true
          },
          "keyRotationDays": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 30,
            "maximum": 365,
            "description": "Required API key rotation interval in days.",
            "example": 90
          },
          "oauthClientCredentialsEnabled": {
            "type": "boolean",
            "description": "Indicates OAuth client-credentials support is enabled for this partner.",
            "example": false
          },
          "soc2EvidenceUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "HTTPS link to SOC 2 evidence or trust-center artifact."
          },
          "pentestEvidenceUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "HTTPS link to penetration-test evidence."
          },
          "lastRotationAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EnterpriseSecurityUpdateRequest": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EnterpriseSecurityPolicy"
          }
        ]
      },
      "OAuthTokenRequest": {
        "type": "object",
        "required": [
          "client_secret"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "client_credentials"
            ],
            "default": "client_credentials"
          },
          "client_id": {
            "type": "string",
            "description": "Optional partner client identifier. Defaults to the authenticated key document id."
          },
          "client_secret": {
            "type": "string",
            "description": "Partner API key used as the client secret."
          },
          "scope": {
            "type": "string",
            "description": "Space-delimited scopes requested for the short-lived access token.",
            "example": "trading:write reports:read"
          }
        }
      },
      "OAuthTokenResponse": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "example": "ms_oauth_..."
          },
          "token_type": {
            "type": "string",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "example": 900
          },
          "scope": {
            "type": "string"
          }
        }
      },
      "CertificationStatus": {
        "type": "string",
        "enum": [
          "not_started",
          "in_progress",
          "passed",
          "failed",
          "waived"
        ]
      },
      "CertificationResponse": {
        "type": "object",
        "properties": {
          "partnerId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "in_progress",
              "go_live_requested",
              "approved"
            ]
          },
          "checks": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "status": {
                  "$ref": "#/components/schemas/CertificationStatus"
                },
                "evidenceUrl": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri"
                },
                "notes": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "updatedAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "updatedBy": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "checklist": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                },
                "owner": {
                  "type": "string",
                  "enum": [
                    "partner",
                    "mystocks"
                  ]
                }
              }
            }
          },
          "progress": {
            "type": "object",
            "properties": {
              "required": {
                "type": "integer"
              },
              "completed": {
                "type": "integer"
              },
              "percent": {
                "type": "integer"
              }
            }
          },
          "goLiveRequestedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "approvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CertificationUpdateRequest": {
        "type": "object",
        "required": [
          "checks"
        ],
        "properties": {
          "checks": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": [
                "status"
              ],
              "properties": {
                "status": {
                  "$ref": "#/components/schemas/CertificationStatus"
                },
                "evidenceUrl": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uri"
                },
                "notes": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "SandboxDepositRequest": {
        "type": "object",
        "required": [
          "subAccountId",
          "amount"
        ],
        "properties": {
          "subAccountId": {
            "type": "string",
            "description": "MyStocks sub-account ID to credit. Must belong to your partner key.",
            "example": "usr_test_abc123"
          },
          "amount": {
            "type": "number",
            "description": "Virtual USD to credit. No real funds move. Max 1,000,000.",
            "example": 10000
          },
          "note": {
            "type": "string",
            "description": "Optional label shown in the admin log and the deposit.confirmed webhook payload."
          }
        }
      },
      "SandboxTradeRequest": {
        "type": "object",
        "required": [
          "symbol",
          "type",
          "quantity"
        ],
        "properties": {
          "symbol": {
            "type": "string",
            "description": "Stock symbol (exchange-qualified or bare ticker).",
            "example": "SCOM.KE"
          },
          "type": {
            "type": "string",
            "enum": [
              "BUY",
              "SELL"
            ],
            "description": "Order direction."
          },
          "quantity": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Number of shares. On the sandbox base URL, use `outcome` for deterministic behavior. The legacy live-key test tool still accepts 100 = instant fill and 999 = instant rejection.",
            "example": 10
          },
          "subAccountId": {
            "type": "string",
            "description": "Sub-account to trade on. BUY checks wallet and escrows; SELL checks held units. Omit to bypass all checks."
          },
          "note": {
            "type": "string",
            "description": "Free-text note shown to the admin for labelling test scenarios."
          },
          "unitPriceUsd": {
            "type": "number",
            "exclusiveMinimum": 0,
            "default": 10,
            "description": "Deterministic execution price used by the isolated sandbox simulator."
          },
          "outcome": {
            "type": "string",
            "default": "PENDING",
            "enum": [
              "PENDING",
              "FILL",
              "PARTIAL_FILL",
              "REJECT",
              "CANCEL",
              "FAIL_SETTLEMENT"
            ],
            "description": "Explicit isolated-sandbox lifecycle outcome. Ignored by the legacy live-key test tool."
          },
          "partialQuantity": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Required when `outcome` is `PARTIAL_FILL`; must be less than `quantity`."
          }
        }
      },
      "RevokeKeyRequest": {
        "type": "object",
        "required": [
          "apiKey"
        ],
        "properties": {
          "apiKey": {
            "type": "string",
            "description": "The pk_live_ key to permanently revoke. Must belong to your account.",
            "example": "pk_live_a1b2c3d4e5f6..."
          }
        }
      },
      "PortfolioSummary": {
        "type": "object",
        "required": [
          "walletBalance",
          "portfolioValue",
          "investedCapital",
          "unrealisedPnl",
          "totalValue"
        ],
        "properties": {
          "walletBalance": {
            "type": "number"
          },
          "portfolioValue": {
            "type": "number"
          },
          "investedCapital": {
            "type": "number"
          },
          "unrealisedPnl": {
            "type": "number"
          },
          "totalValue": {
            "type": "number"
          }
        }
      },
      "PortfolioHolding": {
        "type": "object",
        "required": [
          "symbol",
          "currency",
          "units",
          "avgCost",
          "currentUsdPrice",
          "currentValue",
          "investedCapital",
          "unrealisedPnl",
          "priceIsLive",
          "asOf",
          "stale",
          "priceSource",
          "dataQuality"
        ],
        "properties": {
          "symbol": {
            "type": "string"
          },
          "stockName": {
            "type": [
              "string",
              "null"
            ]
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "type": "string"
          },
          "units": {
            "type": "number"
          },
          "avgCost": {
            "type": "number"
          },
          "currentUsdPrice": {
            "type": "number"
          },
          "localPrice": {
            "type": "number"
          },
          "currentValue": {
            "type": "number"
          },
          "investedCapital": {
            "type": "number"
          },
          "unrealisedPnl": {
            "type": "number"
          },
          "priceIsLive": {
            "type": "boolean",
            "description": "Backwards-compatible freshness flag. True only when the production catalogue price is within the delayed-feed freshness window; never implies real-time tick data."
          },
          "lastPriceUpdate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Upstream capture timestamp for the price used in this valuation."
          },
          "stale": {
            "type": "boolean",
            "description": "True when the price timestamp is missing or older than the published freshness window."
          },
          "priceSource": {
            "type": "string",
            "enum": [
              "PRODUCTION_CATALOGUE",
              "SANDBOX_HOLDING"
            ]
          },
          "dataQuality": {
            "$ref": "#/components/schemas/QuoteDataQuality"
          },
          "id": {
            "type": "string",
            "deprecated": true,
            "description": "Legacy master-account alias."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "deprecated": true,
            "description": "Legacy alias of stockName."
          },
          "costBasis": {
            "type": "number",
            "deprecated": true,
            "description": "Legacy alias of avgCost."
          },
          "amountInvested": {
            "type": "number",
            "deprecated": true,
            "description": "Legacy alias of investedCapital."
          },
          "localCurrency": {
            "type": [
              "string",
              "null"
            ],
            "deprecated": true
          },
          "gainLoss": {
            "type": "number",
            "deprecated": true,
            "description": "Legacy alias of unrealisedPnl."
          },
          "liquidityStatus": {
            "type": [
              "string",
              "null"
            ],
            "deprecated": true
          }
        }
      },
      "PortfolioResponse": {
        "type": "object",
        "required": [
          "accountType",
          "summary",
          "holdings"
        ],
        "properties": {
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "subAccountId": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "summary": {
            "$ref": "#/components/schemas/PortfolioSummary"
          },
          "holdings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PortfolioHolding"
            }
          },
          "count": {
            "type": "integer",
            "description": "Legacy master-account total retained for compatibility."
          },
          "totalValue": {
            "type": "number",
            "deprecated": true
          },
          "totalInvested": {
            "type": "number",
            "deprecated": true
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          }
        }
      },
      "PortfolioHistoryPoint": {
        "type": "object",
        "required": [
          "timestamp",
          "equity",
          "marketValue"
        ],
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "equity": {
            "type": "number",
            "description": "Total account value including cash and holdings."
          },
          "marketValue": {
            "type": "number",
            "description": "Holdings value only."
          }
        }
      },
      "PortfolioHistoryResponse": {
        "type": "object",
        "required": [
          "accountType",
          "scope",
          "baseCurrency",
          "timeframe",
          "period",
          "calculation",
          "cashFlowAdjusted",
          "methodology",
          "points",
          "count"
        ],
        "properties": {
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "MASTER_ACCOUNT",
              "SUBACCOUNT"
            ]
          },
          "subAccountId": {
            "type": "string"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "baseCurrency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "timeframe": {
            "type": "string",
            "enum": [
              "1D"
            ]
          },
          "period": {
            "type": "string",
            "enum": [
              "1M",
              "3M",
              "6M",
              "1Y",
              "ALL"
            ]
          },
          "calculation": {
            "type": "string",
            "enum": [
              "RAW_EQUITY"
            ]
          },
          "cashFlowAdjusted": {
            "type": "boolean",
            "const": false
          },
          "methodology": {
            "type": "string"
          },
          "points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PortfolioHistoryPoint"
            }
          },
          "count": {
            "type": "integer"
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PortfolioPerformancePoint": {
        "type": "object",
        "required": [
          "timestamp",
          "equity",
          "marketValue",
          "netCashFlow",
          "dailyPnl",
          "dailyReturn",
          "cumulativeReturn",
          "benchmarkReturn",
          "excessReturn"
        ],
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "equity": {
            "type": "number"
          },
          "marketValue": {
            "type": "number"
          },
          "netCashFlow": {
            "type": "number",
            "description": "Signed completed external cash flow during the interval."
          },
          "dailyPnl": {
            "type": "number",
            "description": "End equity minus start equity minus net cash flow."
          },
          "dailyReturn": {
            "type": [
              "number",
              "null"
            ],
            "description": "Modified Dietz return for the snapshot interval."
          },
          "cumulativeReturn": {
            "type": "number",
            "description": "Geometrically linked time-weighted return from the first point."
          },
          "benchmarkReturn": {
            "type": [
              "number",
              "null"
            ],
            "description": "Rebased benchmark price return when requested."
          },
          "excessReturn": {
            "type": [
              "number",
              "null"
            ],
            "description": "cumulativeReturn minus benchmarkReturn."
          }
        }
      },
      "PortfolioPerformanceResponse": {
        "type": "object",
        "required": [
          "accountType",
          "scope",
          "baseCurrency",
          "timeframe",
          "period",
          "calculation",
          "cashFlowAdjusted",
          "methodology",
          "benchmark",
          "summary",
          "points",
          "count"
        ],
        "properties": {
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "MASTER_ACCOUNT",
              "SUBACCOUNT"
            ]
          },
          "subAccountId": {
            "type": "string"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "baseCurrency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "timeframe": {
            "type": "string",
            "enum": [
              "1D"
            ]
          },
          "period": {
            "type": "string",
            "enum": [
              "1M",
              "3M",
              "6M",
              "1Y",
              "ALL"
            ]
          },
          "calculation": {
            "type": "string",
            "enum": [
              "DAILY_MODIFIED_DIETZ_LINKED_TWR"
            ]
          },
          "cashFlowAdjusted": {
            "type": "boolean",
            "const": true
          },
          "methodology": {
            "type": "string"
          },
          "benchmark": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "symbol",
                  "methodology"
                ],
                "properties": {
                  "symbol": {
                    "type": "string"
                  },
                  "methodology": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "summary": {
            "type": "object",
            "required": [
              "startEquity",
              "endEquity",
              "netCashFlow",
              "totalPnl",
              "totalReturn",
              "benchmarkReturn",
              "excessReturn"
            ],
            "properties": {
              "startEquity": {
                "type": "number"
              },
              "endEquity": {
                "type": "number"
              },
              "netCashFlow": {
                "type": "number"
              },
              "totalPnl": {
                "type": "number"
              },
              "totalReturn": {
                "type": "number"
              },
              "benchmarkReturn": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "excessReturn": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "points": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PortfolioPerformancePoint"
            }
          },
          "count": {
            "type": "integer"
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "AssetCapabilities": {
        "type": "object",
        "required": [
          "buy",
          "sell",
          "subscribe",
          "redeem",
          "fractional",
          "short",
          "margin"
        ],
        "properties": {
          "buy": {
            "type": "boolean"
          },
          "sell": {
            "type": "boolean"
          },
          "subscribe": {
            "type": "boolean"
          },
          "redeem": {
            "type": "boolean"
          },
          "fractional": {
            "type": "boolean"
          },
          "short": {
            "type": "boolean"
          },
          "margin": {
            "type": "boolean"
          }
        }
      },
      "AssetOrderRules": {
        "type": "object",
        "properties": {
          "minQuantity": {
            "type": [
              "number",
              "null"
            ]
          },
          "minNotional": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "amount",
                  "currency"
                ],
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "currency": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "quantityIncrement": {
            "type": "number"
          },
          "quantityPrecision": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6
          },
          "lotSize": {
            "type": [
              "number",
              "null"
            ]
          },
          "tickSize": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "FractionalEligibility": {
        "type": "object",
        "required": [
          "fractional",
          "minOrderSize",
          "quantityIncrement",
          "quantityPrecision",
          "supportedSizingModes"
        ],
        "properties": {
          "fractional": {
            "type": "boolean",
            "description": "Whether the instrument supports fractional units."
          },
          "minOrderSize": {
            "type": [
              "number",
              "null"
            ],
            "description": "Minimum quantity or subscription amount configured for this instrument."
          },
          "quantityIncrement": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Smallest accepted quantity step."
          },
          "quantityPrecision": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6,
            "description": "Maximum decimal places implied by quantityIncrement."
          },
          "supportedSizingModes": {
            "type": "array",
            "minItems": 1,
            "uniqueItems": true,
            "items": {
              "type": "string",
              "enum": [
                "QUANTITY",
                "CASH_VALUE"
              ]
            }
          }
        }
      },
      "Asset": {
        "type": "object",
        "required": [
          "assetId",
          "sourceId",
          "assetClass",
          "currency",
          "status",
          "tradable",
          "fractionable",
          "shortable",
          "marginable",
          "capabilities",
          "fractionalEligibility",
          "orderRules"
        ],
        "properties": {
          "assetId": {
            "type": "string",
            "example": "stock:SCOM.KE"
          },
          "sourceId": {
            "type": "string"
          },
          "symbol": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "assetClass": {
            "type": "string",
            "enum": [
              "STOCK",
              "ETF",
              "BOND",
              "FUND"
            ]
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ]
          },
          "exchangeMic": {
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "statusReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "tradable": {
            "type": "boolean"
          },
          "fractionable": {
            "type": "boolean"
          },
          "shortable": {
            "type": "boolean",
            "const": false
          },
          "marginable": {
            "type": "boolean",
            "const": false
          },
          "minOrderSize": {
            "type": [
              "number",
              "null"
            ]
          },
          "lotSize": {
            "type": [
              "number",
              "null"
            ]
          },
          "tickSize": {
            "type": [
              "number",
              "null"
            ]
          },
          "settlementCycle": {
            "type": [
              "string",
              "null"
            ]
          },
          "capabilities": {
            "$ref": "#/components/schemas/AssetCapabilities"
          },
          "fractionalEligibility": {
            "$ref": "#/components/schemas/FractionalEligibility"
          },
          "orderRules": {
            "$ref": "#/components/schemas/AssetOrderRules"
          },
          "settlement": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "cycle",
                  "cycleDays"
                ],
                "properties": {
                  "cycle": {
                    "type": "string"
                  },
                  "cycleDays": {
                    "type": "integer"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "StockSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "SCOM.KE",
            "description": "Unique stock identifier."
          },
          "symbol": {
            "type": "string",
            "example": "SCOM.KE",
            "description": "Exchange-qualified ticker symbol."
          },
          "name": {
            "type": "string",
            "example": "Safaricom PLC",
            "description": "Full company name."
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "example": "safaricom",
            "description": "URL-safe identifier. Interchangeable with symbol."
          },
          "exchange": {
            "type": "string",
            "example": "NSE",
            "description": "Exchange code: NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM."
          },
          "exchangeMic": {
            "type": [
              "string",
              "null"
            ],
            "example": "XNAI",
            "description": "ISO 10383 operating Market Identifier Code for the listing exchange."
          },
          "currency": {
            "type": "string",
            "example": "KES",
            "description": "Local trading currency."
          },
          "sector": {
            "type": [
              "string",
              "null"
            ],
            "example": "Telecommunications"
          },
          "assetType": {
            "type": "string",
            "enum": [
              "STOCK",
              "ETF"
            ]
          },
          "listingStatus": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "SUSPENDED",
              "DELISTED"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "example": 16.5,
            "description": "Latest price in local currency."
          },
          "usdPrice": {
            "type": [
              "number",
              "null"
            ],
            "example": 0.1274,
            "description": "Latest price converted to USD at live spot rate."
          },
          "change": {
            "type": [
              "number",
              "null"
            ],
            "example": 0.25,
            "description": "Absolute price change from previous close."
          },
          "changePct": {
            "type": [
              "number",
              "null"
            ],
            "example": 1.54,
            "description": "Percentage price change from previous close."
          },
          "open": {
            "type": [
              "number",
              "null"
            ],
            "description": "Session opening price in local currency."
          },
          "dayHigh": {
            "type": [
              "number",
              "null"
            ],
            "description": "Intraday high in local currency."
          },
          "dayLow": {
            "type": [
              "number",
              "null"
            ],
            "description": "Intraday low in local currency."
          },
          "volume": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Current session trading volume."
          },
          "previousClose": {
            "type": [
              "number",
              "null"
            ],
            "description": "Prior session closing price."
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://mystocks.africa/logos/scom-ke.svg",
            "description": "Self-hosted SVG logo. Safe to use as img src."
          },
          "lastPriceUpdate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "marketStatus": {
            "type": "string",
            "description": "Exchange state at quote time: OPEN or CLOSED. Present on /market/quotes and /market/movers responses."
          },
          "bid": {
            "type": [
              "number",
              "null"
            ],
            "description": "Best bid. Currently always null (no order-book depth in the data source)."
          },
          "ask": {
            "type": [
              "number",
              "null"
            ],
            "description": "Best ask. Currently always null (no order-book depth in the data source)."
          },
          "dataFreshnessSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Seconds since the last price update. Present on /market/quotes and /market/movers responses."
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Timestamp of the exchange observation — NOT when the API answered. African equity data is 15-minute delayed and the API refresh target is 900 seconds, so display and evaluate freshness against this value.\n"
          },
          "stale": {
            "type": "boolean",
            "description": "True when the price is older than two refresh intervals (30 minutes). The feed for this symbol is genuinely behind — do not trust the price for pricing a trade.\n"
          },
          "dataQuality": {
            "$ref": "#/components/schemas/QuoteDataQuality"
          }
        },
        "description": "Canonical stock/quote shape returned by /stocks, /stocks/{symbol}, /market/quotes, and /market/movers. NOTE: /market/quotes and /market/movers also emit DEPRECATED snake_case aliases (usd_price, prev_close, change_pct, asset_type, listing_status, high, low, market_status, timestamp, data_freshness_seconds, logo) for backward compatibility — these are scheduled for removal on 2026-10-01. Use the camelCase fields above.\n"
      },
      "TaxLots": {
        "type": "object",
        "description": "Open tax lots (remaining acquired quantity, cost basis, acquisition date).",
        "properties": {
          "subAccountId": {
            "type": [
              "string",
              "null"
            ]
          },
          "accountType": {
            "type": "string"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "count": {
            "type": "integer"
          },
          "totalCostUsd": {
            "type": "number"
          },
          "lots": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "lotId": {
                  "type": "string",
                  "description": "The acquiring order id."
                },
                "symbol": {
                  "type": "string"
                },
                "exchange": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "quantity": {
                  "type": "number",
                  "description": "Remaining open quantity."
                },
                "originalQuantity": {
                  "type": "number"
                },
                "costBasisPerUnitUsd": {
                  "type": "number"
                },
                "remainingCostUsd": {
                  "type": "number"
                },
                "acquiredAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "acquisitionFeeUsd": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "RealizedGains": {
        "type": "object",
        "description": "Realized capital gains/losses, each disposal FIFO-matched to its lot.",
        "properties": {
          "subAccountId": {
            "type": [
              "string",
              "null"
            ]
          },
          "accountType": {
            "type": "string"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "method": {
            "type": "string",
            "enum": [
              "FIFO"
            ]
          },
          "count": {
            "type": "integer"
          },
          "totalProceedsUsd": {
            "type": "number"
          },
          "totalCostUsd": {
            "type": "number"
          },
          "realizedGainUsd": {
            "type": "number"
          },
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "to": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "disposals": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "symbol": {
                  "type": "string"
                },
                "exchange": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "quantity": {
                  "type": "number"
                },
                "acquiredAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "disposedAt": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "holdingPeriodDays": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "costBasisUsd": {
                  "type": "number"
                },
                "proceedsUsd": {
                  "type": "number"
                },
                "gainUsd": {
                  "type": "number"
                },
                "method": {
                  "type": "string",
                  "enum": [
                    "FIFO"
                  ]
                },
                "buyOrderId": {
                  "type": "string"
                },
                "sellOrderId": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "PartnerPricing": {
        "type": "object",
        "description": "Self-serve markup configuration.",
        "properties": {
          "defaultMarkupBps": {
            "type": "number",
            "description": "Default markup (bps) when no override matches."
          },
          "overrides": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scope": {
                  "type": "string",
                  "enum": [
                    "symbol",
                    "exchange",
                    "assetClass"
                  ]
                },
                "key": {
                  "type": "string"
                },
                "markupBps": {
                  "type": "number"
                }
              }
            }
          },
          "promo": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "markupBps": {
                "type": "number"
              },
              "until": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "promoActive": {
            "type": "boolean",
            "description": "Whether the promo window is currently in effect."
          },
          "maxMarkupBps": {
            "type": "number",
            "description": "Platform cap on any markupBps."
          },
          "legacyMarkupBps": {
            "type": "number",
            "description": "The flat markup used when no pricing config is set."
          }
        }
      },
      "FloatStatus": {
        "type": "object",
        "description": "Master-account funding headroom.",
        "properties": {
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "masterBalanceUsd": {
            "type": "number",
            "description": "Current master wallet balance (negative when drawn on the credit line)."
          },
          "pendingTopupsUsd": {
            "type": "number",
            "description": "Sum of PENDING top-up requests awaiting MyStocks confirmation."
          },
          "pendingPayoutsUsd": {
            "type": "number",
            "description": "Sum of PENDING payout requests awaiting MyStocks processing."
          },
          "creditLimitUsd": {
            "type": "number",
            "description": "Credit line — how far the master balance may go negative while a remittance is in flight (MyStocks-set; 0 = pre-funded only)."
          },
          "outstandingCreditUsd": {
            "type": "number",
            "description": "Credit currently drawn (present when masterBalanceUsd is negative)."
          },
          "availableToFundUsd": {
            "type": "number",
            "description": "masterBalanceUsd + creditLimitUsd — the total still available to fund sub-accounts."
          },
          "lowBalanceThresholdUsd": {
            "type": "number",
            "description": "Master balance below which a float.low webhook fires (partner-set via PATCH /float)."
          },
          "minimumFloatUsd": {
            "type": "number",
            "description": "Partner treasury policy floor for the master account."
          },
          "floatStatus": {
            "type": "string",
            "enum": [
              "HEALTHY",
              "LOW",
              "DEPLETED",
              "ON_CREDIT"
            ]
          },
          "treasuryStatus": {
            "type": "string",
            "enum": [
              "HEALTHY",
              "LOW",
              "DEPLETED",
              "ON_CREDIT",
              "BELOW_MINIMUM"
            ]
          },
          "reminderSettings": {
            "type": "object",
            "properties": {
              "autoTopupReminderEnabled": {
                "type": "boolean"
              },
              "recipients": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "email"
                }
              },
              "frequencyHours": {
                "type": "integer"
              },
              "shouldRemindNow": {
                "type": "boolean"
              }
            }
          },
          "exposure": {
            "type": "object",
            "description": "Partner treasury exposure dashboard.",
            "properties": {
              "subAccountCount": {
                "type": "integer"
              },
              "pendingTopupsUsd": {
                "type": "number"
              },
              "pendingPayoutsUsd": {
                "type": "number"
              },
              "pendingOrderExposureUsd": {
                "type": "number"
              },
              "outstandingCreditUsd": {
                "type": "number"
              },
              "availableToFundUsd": {
                "type": "number"
              },
              "netLiquidityUsd": {
                "type": "number"
              },
              "pendingCreditLimitRequests": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "requestId": {
                      "type": "string"
                    },
                    "requestedCreditLimitUsd": {
                      "type": "number"
                    },
                    "currentCreditLimitUsd": {
                      "type": "number"
                    },
                    "status": {
                      "type": "string"
                    },
                    "requestedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "BuyingPower": {
        "type": "object",
        "description": "Settlement-aware balance for a master or sub-account.",
        "properties": {
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "subAccountId": {
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "cashBalanceUsd": {
            "type": "number",
            "description": "Current USD wallet balance."
          },
          "buyingPowerUsd": {
            "type": "number",
            "description": "Cash available to place new orders (already net of open-BUY escrow; includes unsettled SELL proceeds)."
          },
          "unsettledUsd": {
            "type": "number",
            "description": "SELL proceeds credited but not yet settled at the exchange (T+N)."
          },
          "withdrawableUsd": {
            "type": "number",
            "description": "Cash available to withdraw — cashBalanceUsd minus unsettledUsd. The free-riding guard."
          },
          "pendingBuyEscrowUsd": {
            "type": "number",
            "description": "Funds locked in open (PENDING) BUY orders."
          },
          "settlements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "amountUsd": {
                  "type": "number"
                },
                "settlesOn": {
                  "type": "string",
                  "format": "date-time"
                },
                "symbol": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "exchange": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "orderId": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "EtfSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "GLD.ZA",
            "description": "Unique ETF identifier."
          },
          "symbol": {
            "type": "string",
            "example": "GLD.ZA",
            "description": "Exchange-qualified ticker symbol."
          },
          "name": {
            "type": "string",
            "example": "1nvest Gold ETF",
            "description": "Full ETF name."
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "example": "gold-etf",
            "description": "URL-safe identifier. Interchangeable with symbol."
          },
          "exchange": {
            "type": "string",
            "example": "JSE",
            "description": "Exchange code: NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM."
          },
          "currency": {
            "type": "string",
            "example": "ZAR",
            "description": "Local trading currency."
          },
          "sector": {
            "type": [
              "string",
              "null"
            ],
            "example": "Precious Metals"
          },
          "assetType": {
            "type": "string",
            "example": "ETF"
          },
          "listingStatus": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "SUSPENDED",
              "DELISTED"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "example": 22000,
            "description": "Latest price in local currency."
          },
          "usdPrice": {
            "type": [
              "number",
              "null"
            ],
            "example": 1180.5,
            "description": "Latest price converted to USD at live spot rate."
          },
          "change": {
            "type": [
              "number",
              "null"
            ],
            "example": 15,
            "description": "Absolute price change from previous close."
          },
          "changePct": {
            "type": [
              "number",
              "null"
            ],
            "example": 0.68,
            "description": "Percentage price change from previous close."
          },
          "open": {
            "type": [
              "number",
              "null"
            ],
            "description": "Session opening price in local currency."
          },
          "dayHigh": {
            "type": [
              "number",
              "null"
            ],
            "description": "Intraday high in local currency."
          },
          "dayLow": {
            "type": [
              "number",
              "null"
            ],
            "description": "Intraday low in local currency."
          },
          "volume": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Current session trading volume."
          },
          "previousClose": {
            "type": [
              "number",
              "null"
            ],
            "description": "Prior session closing price."
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://mystocks.africa/logos/gld-za.svg",
            "description": "Self-hosted SVG logo. Safe to use as img src."
          },
          "lastPriceUpdate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expenseRatio": {
            "type": [
              "number",
              "null"
            ],
            "example": 0.0025,
            "description": "The ETF annual management fee/expense ratio."
          },
          "indexTracked": {
            "type": [
              "string",
              "null"
            ],
            "example": "Gold Spot Price",
            "description": "The underlying index or asset tracked by the ETF."
          },
          "inceptionDate": {
            "type": [
              "string",
              "null"
            ],
            "example": "2010-04-12",
            "description": "The inception date of the fund."
          },
          "topHoldings": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "example": [
              "Physical Gold Bullion"
            ],
            "description": "Top constituents or holdings of the ETF."
          },
          "managementStyle": {
            "type": [
              "string",
              "null"
            ],
            "example": "PASSIVE",
            "description": "PASSIVE or ACTIVE management style."
          },
          "geographicalFocus": {
            "type": [
              "string",
              "null"
            ],
            "example": "Global",
            "description": "Geographical focus of the fund's assets."
          },
          "riskLevel": {
            "type": [
              "string",
              "null"
            ],
            "example": "Medium-High",
            "description": "Risk profile rating of the ETF."
          },
          "distributionFrequency": {
            "type": [
              "string",
              "null"
            ],
            "example": "Semi-Annually",
            "description": "Distribution frequency (e.g. Annually, Semi-Annually, Quarterly, None)."
          },
          "brand": {
            "type": [
              "string",
              "null"
            ],
            "example": "1nvest",
            "description": "Fund sponsor/issuer brand."
          },
          "structure": {
            "type": [
              "string",
              "null"
            ],
            "example": "Mutual Fund / Trust",
            "description": "Legal structure of the ETF fund."
          },
          "replicationMethod": {
            "type": [
              "string",
              "null"
            ],
            "example": "Physical",
            "description": "Physical or Synthetic replication."
          },
          "dividendTreatment": {
            "type": [
              "string",
              "null"
            ],
            "example": "Accumulating",
            "description": "Accumulating or Distributing."
          },
          "primaryAdvisor": {
            "type": [
              "string",
              "null"
            ],
            "example": "Stanlib Crowd",
            "description": "Advisor company of the ETF."
          },
          "issuer": {
            "type": [
              "string",
              "null"
            ],
            "example": "Stanlib",
            "description": "The actual fund manager or issuing house."
          }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string",
            "example": "ord_abc123xyz",
            "description": "Unique order ID. Use to poll status or cancel."
          },
          "symbol": {
            "type": "string",
            "example": "SCOM.KE"
          },
          "name": {
            "type": "string",
            "example": "Safaricom PLC"
          },
          "exchange": {
            "type": "string",
            "example": "NSE"
          },
          "orderType": {
            "type": "string",
            "enum": [
              "MARKET",
              "LIMIT",
              "STOP",
              "STOP_LIMIT"
            ],
            "description": "Order type. MARKET orders are quote-gated; LIMIT/STOP/STOP_LIMIT rest as WORKING until triggered.",
            "example": "LIMIT"
          },
          "limitPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Limit price in the stock's local currency (LIMIT / STOP_LIMIT only)."
          },
          "stopPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Stop price in the stock's local currency (STOP / STOP_LIMIT only)."
          },
          "cancelable": {
            "type": "boolean",
            "description": "Whether the order can be cancelled now (DELETE). True while PENDING or WORKING."
          },
          "replaceable": {
            "type": "boolean",
            "description": "Whether the order can be modified now (PATCH). True only for a WORKING resting order."
          },
          "type": {
            "type": "string",
            "enum": [
              "BUY",
              "SELL"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "WORKING",
              "PARTIALLY_FILLED",
              "PROCESSING",
              "COMPLETED",
              "FILLED",
              "REJECTED",
              "CANCELLED",
              "EXPIRED"
            ],
            "description": "PENDING after submission. WORKING = resting LIMIT/STOP/STOP_LIMIT order waiting for its trigger price (becomes PENDING on trigger). PARTIALLY_FILLED and FILLED are the canonical execution states. REJECTED, CANCELLED, and EXPIRED are terminal outcomes. PROCESSING is a legacy transition alias and COMPLETED is a v1 compatibility alias of FILLED; neither represents custody settlement. Settlement is tracked separately as PENDING, SETTLED, or FAILED."
          },
          "quoteId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pre-trade quote ID consumed by this order."
          },
          "quoteExpiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "quoteUsdPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "clientOrderId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Partner-supplied order reference."
          },
          "omsStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "RECEIVED",
              "FILLED",
              "REJECTED",
              "CANCELLED",
              "null"
            ],
            "description": "Canonical OMS lifecycle status."
          },
          "timeInForce": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "DAY",
              "GTC",
              "GTD",
              "null"
            ]
          },
          "filledQuantity": {
            "type": "number"
          },
          "remainingQuantity": {
            "type": "number"
          },
          "averageFillPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "executionVenue": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastExecutionReportId": {
            "type": [
              "string",
              "null"
            ]
          },
          "executionSla": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ExecutionSla"
              },
              {
                "type": "null"
              }
            ],
            "description": "Live execution SLA clock for orders that have entered the PENDING dealing-desk queue. Null while a resting order is still WORKING."
          },
          "quantity": {
            "type": "integer"
          },
          "priceAtOrder": {
            "type": "number",
            "description": "Stock price in local currency at submission."
          },
          "usdPriceAtOrder": {
            "type": "number",
            "description": "USD equivalent of priceAtOrder."
          },
          "fxRateAtOrder": {
            "type": [
              "number",
              "null"
            ],
            "description": "Managed FX rate used for local-to-USD conversion at order submission."
          },
          "fxSource": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "MYSTOCKS_MANAGED_FX",
              "PARTNER_PROVIDED_LEGACY",
              "null"
            ]
          },
          "fxRateAsOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "fxConversion": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/FxConversion"
              },
              {
                "type": "null"
              }
            ]
          },
          "fee": {
            "type": "number",
            "description": "Total fee in USD (base + partner markup)."
          },
          "baseFee": {
            "type": "number",
            "description": "MyStocks base broker fee (0.75% of gross)."
          },
          "partnerMarkupFee": {
            "type": "number",
            "description": "Your markup fee component. Zero if markupBps = 0."
          },
          "totalAmount": {
            "type": "number",
            "description": "BUY: gross + fee. SELL: gross - fee."
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "localCurrency": {
            "type": "string",
            "example": "KES"
          },
          "rejectionCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Structured rejection code. Populated when status = REJECTED.",
            "enum": [
              "INSUFFICIENT_FUNDS",
              "KYC_REQUIRED",
              "MARKET_CLOSED",
              "COMPLIANCE_HOLD",
              "TECHNICAL_ISSUE",
              "OTHER",
              "null"
            ]
          },
          "rejectionReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free-text rejection detail provided by the MyStocks internal-book execution service. Populated when status = REJECTED."
          },
          "cancelledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "settledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set when custody settlement completes; independent of the canonical FILLED execution state. Legacy completed records that pre-date this field return their immutable completion/creation timestamp as compatibility evidence."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ExecutionSla": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "enum": [
              "LIVE_DEALING_DESK"
            ],
            "description": "Execution path covered by this SLA."
          },
          "minMinutes": {
            "type": "integer",
            "example": 1,
            "description": "Earliest expected live execution window, in minutes."
          },
          "maxMinutes": {
            "type": "integer",
            "example": 5,
            "description": "Maximum live execution window, in minutes."
          },
          "targetMinutes": {
            "type": "integer",
            "example": 5,
            "description": "Deadline used by monitoring for breach detection."
          },
          "clockStartedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the order entered the live execution queue."
          },
          "targetFillBy": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Latest expected fill or rejection timestamp."
          },
          "status": {
            "type": "string",
            "enum": [
              "WITHIN_SLA",
              "BREACHED",
              "MET",
              "CANCELLED"
            ]
          },
          "breachedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastCheckedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "FxConversion": {
        "type": "object",
        "properties": {
          "sourceCurrency": {
            "type": "string",
            "example": "KES"
          },
          "sourceAmount": {
            "type": "number",
            "example": 65000
          },
          "targetCurrency": {
            "type": "string",
            "example": "USD"
          },
          "targetAmount": {
            "type": "number",
            "example": 500
          },
          "rate": {
            "type": "number",
            "example": 130
          },
          "rateConvention": {
            "type": "string",
            "example": "KES per USD"
          },
          "source": {
            "type": "string",
            "enum": [
              "MYSTOCKS_MANAGED_FX",
              "PARTNER_PROVIDED_LEGACY"
            ]
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WalletView": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "example": "KES"
          },
          "balance": {
            "type": "number",
            "example": 65000
          },
          "available": {
            "type": "number",
            "example": 65000
          },
          "ledgerCurrency": {
            "type": "string",
            "example": "USD"
          },
          "ledgerBalance": {
            "type": "number",
            "example": 500
          },
          "fxRate": {
            "type": "number",
            "example": 130
          },
          "fxSource": {
            "type": "string",
            "example": "MYSTOCKS_MANAGED_FX"
          },
          "fxRateAsOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ManagedFx": {
        "type": "object",
        "properties": {
          "ledgerCurrency": {
            "type": "string",
            "example": "USD"
          },
          "source": {
            "type": "string",
            "example": "MYSTOCKS_MANAGED_FX"
          },
          "rateConvention": {
            "type": "string",
            "example": "local currency units per USD"
          },
          "supportedCurrencies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "asOf": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ExecutionReport": {
        "type": "object",
        "properties": {
          "executionReportId": {
            "type": "string"
          },
          "journalVersion": {
            "type": [
              "integer",
              "null"
            ]
          },
          "immutable": {
            "type": "boolean"
          },
          "eventHashAlgorithm": {
            "type": [
              "string",
              "null"
            ]
          },
          "eventHash": {
            "type": [
              "string",
              "null"
            ]
          },
          "orderId": {
            "type": "string"
          },
          "clientOrderId": {
            "type": [
              "string",
              "null"
            ]
          },
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "subAccountId": {
            "type": [
              "string",
              "null"
            ]
          },
          "reportType": {
            "type": "string",
            "enum": [
              "ORDER_RECEIVED",
              "FILL",
              "REJECT",
              "CANCEL",
              "REPLACE",
              "CORRECTION"
            ]
          },
          "orderStatus": {
            "type": "string",
            "enum": [
              "RECEIVED",
              "FILLED",
              "REJECTED",
              "CANCELLED",
              "REPLACED",
              "WORKING"
            ]
          },
          "side": {
            "type": "string",
            "enum": [
              "BUY",
              "SELL"
            ]
          },
          "symbol": {
            "type": "string"
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ]
          },
          "requestedQuantity": {
            "type": "number"
          },
          "filledQuantity": {
            "type": "number"
          },
          "remainingQuantity": {
            "type": "number"
          },
          "fillPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "averageFillPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "feeAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "netAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "executionVenue": {
            "type": [
              "string",
              "null"
            ]
          },
          "actorType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "PARTNER_KEY",
              "ADMIN_USER",
              "SYSTEM_JOB",
              null
            ]
          },
          "actorId": {
            "type": [
              "string",
              "null"
            ]
          },
          "requestId": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "correctsExecutionReportId": {
            "type": [
              "string",
              "null"
            ]
          },
          "synthetic": {
            "type": "boolean",
            "description": "True only for a read-time compatibility FILL derived from a legacy filled order that predates the immutable execution journal."
          },
          "sourceEvidence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "LEGACY_ORDER_STATE",
              null
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "occurredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recordedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ExecutionReportsResponse": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string"
          },
          "subAccountId": {
            "type": [
              "string",
              "null"
            ]
          },
          "executions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExecutionReport"
            }
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "SubAccount": {
        "type": "object",
        "properties": {
          "subAccountId": {
            "type": "string",
            "example": "usr_abc123",
            "description": "MyStocks internal ID. Use in /users/{userId}/... paths."
          },
          "externalId": {
            "type": "string",
            "example": "usr_8821",
            "description": "Your own user identifier as set at creation."
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ],
            "example": "Alice K."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "example": "alice@yourapp.com"
          },
          "kycStatus": {
            "type": "string",
            "enum": [
              "NONE",
              "PENDING",
              "VERIFIED",
              "REJECTED"
            ],
            "description": "VERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted."
          },
          "kycLevel": {
            "type": "string",
            "enum": [
              "NONE",
              "BASIC",
              "FULL"
            ],
            "description": "BASIC = ID verified. FULL = enhanced due diligence."
          },
          "kycRejectionCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stable rejection code when kycStatus is REJECTED."
          },
          "kycRejectionReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Customer-safe remediation guidance when kycStatus is REJECTED."
          },
          "kycReviewedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Timestamp of the rejection decision."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "frozen",
              "closed"
            ]
          },
          "walletBalance": {
            "type": "number",
            "example": 1250,
            "description": "Current USD wallet balance."
          }
        }
      },
      "CloseSubAccountResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Sub-account closed. Residual cash transferred to partner master wallet."
          },
          "subAccountId": {
            "type": "string",
            "example": "usr_abc123"
          },
          "externalId": {
            "type": "string",
            "example": "user_42"
          },
          "status": {
            "type": "string",
            "enum": [
              "closed"
            ]
          },
          "residualTransferUsd": {
            "type": "number",
            "description": "USD cash moved back to the partner master wallet during closure.",
            "example": 42.5
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "newMasterBalance": {
            "type": "number",
            "description": "Present when residual cash was transferred.",
            "example": 10042.5
          },
          "alreadyClosed": {
            "type": "boolean",
            "description": "true when the sub-account was already closed."
          }
        }
      },
      "WebhookRegistration": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "wh_abc123",
            "description": "Unique webhook ID."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://yourapp.com/webhooks/mystocks"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Subscribed event types."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled"
            ]
          }
        }
      },
      "Bar": {
        "type": "object",
        "description": "A single OHLCV candle. Shared by /market/ohlcv and /market/snapshot.",
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 start timestamp for the candle period.",
            "example": "2026-07-15T00:00:00.000Z"
          },
          "open": {
            "type": [
              "number",
              "null"
            ],
            "description": "Open price (local currency)."
          },
          "high": {
            "type": [
              "number",
              "null"
            ],
            "description": "High price (local currency)."
          },
          "low": {
            "type": [
              "number",
              "null"
            ],
            "description": "Low price (local currency)."
          },
          "close": {
            "type": [
              "number",
              "null"
            ],
            "description": "Close price (local currency).",
            "example": 35.5
          },
          "volume": {
            "type": "integer",
            "description": "Trading volume for the period. 0 if not recorded.",
            "example": 45394
          },
          "phase": {
            "type": "string",
            "description": "Session/intraday bars only — which of the 3 daily samples this is (`open`, `mid`, or `close`). Absent on daily (`1D`) bars.\n",
            "enum": [
              "open",
              "mid",
              "close"
            ]
          }
        }
      },
      "Snapshot": {
        "type": "object",
        "description": "A one-call market bundle for a symbol.",
        "properties": {
          "symbol": {
            "type": "string",
            "example": "SCOM.KE"
          },
          "quote": {
            "allOf": [
              {
                "$ref": "#/components/schemas/StockSummary"
              }
            ],
            "description": "The delayed quote, carrying asOf, stale, and dataQuality."
          },
          "dailyBar": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Bar"
              },
              {
                "type": "null"
              }
            ],
            "description": "Today's daily bar (derived from the latest 15-minute-delayed observation until the EOD candle is written)."
          },
          "prevDailyBar": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Bar"
              },
              {
                "type": "null"
              }
            ],
            "description": "The previous trading day's daily bar."
          },
          "intradayBar": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Bar"
              },
              {
                "type": "null"
              }
            ],
            "description": "The latest coarse intraday sample (open/mid/close). null until capture has run."
          }
        }
      },
      "Candle": {
        "type": "object",
        "description": "A chart-ready OHLCV candle in the instrument's listing currency.",
        "required": [
          "timestamp",
          "open",
          "high",
          "low",
          "close",
          "volume",
          "ohlcAvailable",
          "volumeAvailable"
        ],
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "UTC start of the 15-minute, hourly, daily, ISO-week, or calendar-month bucket.",
            "example": "2026-08-03T00:00:00.000Z"
          },
          "open": {
            "type": "number",
            "example": 16.2
          },
          "high": {
            "type": "number",
            "example": 16.75
          },
          "low": {
            "type": "number",
            "example": 16.1
          },
          "close": {
            "type": "number",
            "example": 16.5
          },
          "volume": {
            "type": "integer",
            "description": "Reported or sampled volume in the candle bucket. Check volumeAvailable before using it.",
            "example": 2450000
          },
          "ohlcAvailable": {
            "type": "boolean",
            "description": "True when OHLC was reported or sampled; false when open/high/low are close-derived fallbacks."
          },
          "volumeAvailable": {
            "type": "boolean",
            "description": "True when the source supplied volume for every observation in this candle."
          }
        }
      },
      "Meta": {
        "type": "object",
        "description": "Standard response metadata included on every market-data envelope.",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for this request. Include in support tickets for tracing.",
            "example": "4f6e1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 UTC timestamp of when the response was generated.",
            "example": "2026-06-04T10:23:45.123Z"
          },
          "exchange": {
            "type": "string",
            "description": "Exchange code the request was scoped to, when applicable.",
            "example": "NSE"
          },
          "symbol": {
            "type": "string",
            "description": "Ticker symbol the request was scoped to, when applicable.",
            "example": "SCOM.KE"
          }
        }
      },
      "ReconciliationAccount": {
        "type": "object",
        "description": "One account (master or sub-account) covered by the reconciliation report.",
        "properties": {
          "userId": {
            "type": "string"
          },
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your identifier for the sub-account, if set. Null for the master account."
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ReconciliationCashEntry": {
        "type": "object",
        "description": "One wallet transaction in the reconciliation period. The fees and dividends arrays reuse this shape (filtered to type FEE and DISTRIBUTION/DIVIDEND respectively).",
        "properties": {
          "userId": {
            "type": "string"
          },
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "transactionId": {
            "type": "string"
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Transaction type, e.g. DEPOSIT, WITHDRAWAL, INVEST, FEE, DISTRIBUTION."
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "direction": {
            "type": [
              "string",
              "null"
            ],
            "description": "CREDIT or DEBIT where recorded."
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "settledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ReconciliationSecurityPosition": {
        "type": "object",
        "description": "One holding row in the securities ledger, valued in USD.",
        "properties": {
          "userId": {
            "type": "string"
          },
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "symbol": {
            "type": "string",
            "example": "SCOM.KE"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ]
          },
          "sourceType": {
            "type": "string",
            "example": "LISTED_STOCK"
          },
          "sourceId": {
            "type": "string"
          },
          "units": {
            "type": "number"
          },
          "reservedUnits": {
            "type": "number",
            "description": "Units locked by open SELL orders."
          },
          "costBasis": {
            "type": [
              "number",
              "null"
            ]
          },
          "amountInvested": {
            "type": "number"
          },
          "currentValue": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "localCurrency": {
            "type": [
              "string",
              "null"
            ]
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ReconciliationUnsettledTrade": {
        "type": "object",
        "description": "One open (PENDING/PROCESSING) trade order awaiting settlement.",
        "properties": {
          "userId": {
            "type": "string"
          },
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "orderId": {
            "type": "string"
          },
          "clientOrderId": {
            "type": [
              "string",
              "null"
            ]
          },
          "quoteId": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "BUY",
              "SELL",
              null
            ]
          },
          "symbol": {
            "type": "string"
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "example": "PENDING"
          },
          "quantity": {
            "type": "number"
          },
          "filledQuantity": {
            "type": "number"
          },
          "remainingQuantity": {
            "type": "number"
          },
          "totalAmount": {
            "type": "number"
          },
          "feeAmount": {
            "type": "number"
          },
          "settlementUsdPrice": {
            "type": [
              "number",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "settledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "omsStatus": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ReconciliationCorporateAction": {
        "type": "object",
        "description": "Corporate action affecting a symbol held by a reported account in the period.",
        "properties": {
          "id": {
            "type": "string"
          },
          "symbol": {
            "type": "string"
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "example": "SPLIT"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "exDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "recordDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "payDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ReconciliationCustodyPosition": {
        "type": "object",
        "description": "Custody register entry for an account's beneficially-owned position.",
        "properties": {
          "positionId": {
            "type": "string"
          },
          "userId": {
            "type": "string"
          },
          "accountType": {
            "type": "string",
            "enum": [
              "MASTER",
              "SUBACCOUNT"
            ]
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ]
          },
          "instrumentId": {
            "type": [
              "string",
              "null"
            ]
          },
          "legalHolder": {
            "type": [
              "string",
              "null"
            ]
          },
          "beneficialOwner": {
            "type": [
              "string",
              "null"
            ]
          },
          "unitsHeld": {
            "type": "number"
          },
          "averageCost": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "accruedInterest": {
            "type": [
              "number",
              "null"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CorporateActionOption": {
        "type": "object",
        "required": [
          "id",
          "label"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "ACCEPT_ALL"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "requiresUnits": {
            "type": "boolean",
            "default": false
          },
          "allowPartial": {
            "type": "boolean",
            "default": false
          },
          "isDefault": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "CorporateAction": {
        "type": "object",
        "required": [
          "id",
          "symbol",
          "type",
          "title",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "ca_abc123"
          },
          "symbol": {
            "type": "string",
            "example": "SCOM.KE"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ],
            "example": "NSE"
          },
          "type": {
            "type": "string",
            "example": "RIGHTS_ISSUE"
          },
          "status": {
            "type": "string",
            "example": "OPEN"
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "terms": {
            "type": [
              "string",
              "null"
            ]
          },
          "recordDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "electionDeadline": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "effectiveDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "defaultOptionId": {
            "type": [
              "string",
              "null"
            ]
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CorporateActionOption"
            }
          },
          "documents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "authoritative": {
            "type": "boolean"
          }
        }
      },
      "GovernanceInstruction": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "PENDING_CUSTODIAN",
              "ACCEPTED",
              "REJECTED"
            ]
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "submittedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "amendedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "custodianReference": {
            "type": [
              "string",
              "null"
            ]
          },
          "custodianReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "acceptedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "confirmationDocumentUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "PartnerMember": {
        "type": "object",
        "required": [
          "id",
          "email",
          "roleIds",
          "permissions",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "uid": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "roleIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "INVITED",
              "ACTIVE",
              "SUSPENDED",
              "REMOVED"
            ]
          },
          "activeSessionCount": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "PartnerRole": {
        "type": "object",
        "required": [
          "id",
          "name",
          "permissions",
          "system"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "system": {
            "type": "boolean"
          }
        }
      },
      "PartnerSupportTicket": {
        "type": "object",
        "required": [
          "id",
          "ticketNumber",
          "subject",
          "category",
          "severity",
          "status"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "ticketNumber": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "P0",
              "P1",
              "P2",
              "P3"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "OPEN",
              "ACKNOWLEDGED",
              "INVESTIGATING",
              "WAITING_ON_PARTNER",
              "WAITING_ON_MYSTOCKS",
              "RESOLVED",
              "CLOSED"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "PRODUCTION",
              "SANDBOX"
            ]
          },
          "requestId": {
            "type": [
              "string",
              "null"
            ]
          },
          "acknowledgementDueAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextUpdateDueAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ShareholderMeeting": {
        "type": "object",
        "required": [
          "id",
          "symbol",
          "meetingType",
          "status",
          "title",
          "meetingDate",
          "recordDate",
          "votingDeadline"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "mtg_abc123"
          },
          "symbol": {
            "type": "string",
            "example": "SCOM.KE"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "exchange": {
            "type": [
              "string",
              "null"
            ],
            "example": "NSE"
          },
          "meetingType": {
            "type": "string",
            "enum": [
              "AGM",
              "EGM",
              "COURT",
              "CLASS",
              "OTHER"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "OPEN",
              "CLOSED",
              "CONFIRMED",
              "CANCELLED"
            ]
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "meetingDate": {
            "type": "string",
            "format": "date-time"
          },
          "recordDate": {
            "type": "string",
            "format": "date"
          },
          "votingDeadline": {
            "type": "string",
            "format": "date-time"
          },
          "defaultTreatment": {
            "type": "string"
          },
          "documents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "type": {
                  "type": "string",
                  "example": "PROXY_FORM"
                }
              }
            }
          },
          "resolutions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "title",
                "choices"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "number": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "managementRecommendation": {
                  "type": "string",
                  "enum": [
                    "FOR",
                    "AGAINST",
                    "NONE"
                  ]
                },
                "choices": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "VoteRequest": {
        "type": "object",
        "required": [
          "ballots",
          "acknowledgement"
        ],
        "properties": {
          "ballots": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "resolutionId",
                "choice"
              ],
              "properties": {
                "resolutionId": {
                  "type": "string"
                },
                "choice": {
                  "type": "string",
                  "example": "FOR"
                }
              }
            }
          },
          "acknowledgement": {
            "type": "string",
            "minLength": 1
          }
        }
      }
    }
  },
  "paths": {
    "/register": {
      "servers": [
        {
          "url": "https://mystocks.africa/api/sandbox/v1",
          "description": "Sandbox (registration endpoint lives outside the /partner/ namespace)"
        }
      ],
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Register sandbox account",
        "description": "Creates a sandbox account and returns a **sandbox API key** (`sk_sandbox_…`).\n\n**Authentication:** Pass your Firebase ID token as `Authorization: Bearer <token>`.\nThe recommended flow is to sign in at the [partner dashboard](https://mystocks.africa/partners/login)\nand use its explicit Create/Rotate Key action; the portal performs this authenticated request\nwithout exposing the Firebase token. The token identifies your Firebase account and links the\nsandbox key to your partner profile.\n\n- Your master wallet is seeded with a virtual **$100,000** on first registration.\n- Calling this endpoint again rotates the credential and immediately invalidates the previous sandbox key.\n- The sandbox is fully isolated — no real money moves.\n\nTo go live, complete KYB verification via the partner dashboard and request a production key.\n",
        "security": [
          {
            "FirebaseToken": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/sandbox/v1/register \\\n  -H \"Authorization: Bearer FIREBASE_ID_TOKEN\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/sandbox/v1/register', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer FIREBASE_ID_TOKEN' },\n});\nconst { apiKey, walletBalance } = await res.json();\n// apiKey starts with sk_sandbox_ — use it for all sandbox calls\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post(\n  'https://mystocks.africa/api/sandbox/v1/register',\n  headers={'Authorization': 'Bearer FIREBASE_ID_TOKEN'},\n)\napi_key = r.json()['apiKey']\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Registration successful.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Sandbox account created."
                    },
                    "apiKey": {
                      "type": "string",
                      "example": "sk_sandbox_a1b2c3d4e5f6"
                    },
                    "walletBalance": {
                      "type": "number",
                      "example": 100000
                    },
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "existing": {
                      "type": "boolean",
                      "description": "true if your account already existed and the key was retrieved rather than created.",
                      "example": false
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Firebase ID token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "No partner application on file. Apply at /partners/register first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createRegister"
      }
    },
    "/account": {
      "get": {
        "tags": [
          "Registration"
        ],
        "summary": "Get partner account",
        "description": "Returns your partner profile — tier, wallet balance, API key metadata, and feature flags.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/account \\\n  -H \"Authorization: Bearer pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/account', {\n  headers: { Authorization: 'Bearer pk_live_KEY' },\n});\nconst account = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/account',\n  headers={'Authorization': 'Bearer pk_live_KEY'})\naccount = r.json()\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Partner account details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accountId": {
                      "type": "string"
                    },
                    "businessName": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "wallet": {
                      "type": "object",
                      "properties": {
                        "currency": {
                          "type": "string",
                          "example": "USD"
                        },
                        "balance": {
                          "type": "number"
                        }
                      }
                    },
                    "wallets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WalletView"
                      }
                    },
                    "managedFx": {
                      "$ref": "#/components/schemas/ManagedFx"
                    },
                    "portfolioValue": {
                      "type": "number"
                    },
                    "investedCapital": {
                      "type": "number"
                    },
                    "realizedGainUsd": {
                      "type": "number"
                    },
                    "environment": {
                      "type": "string",
                      "enum": [
                        "production"
                      ]
                    },
                    "pilot": {
                      "$ref": "#/components/schemas/PartnerPilotPolicy"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listAccount"
      }
    },
    "/me": {
      "get": {
        "tags": [
          "Registration"
        ],
        "summary": "My status",
        "description": "Returns the current partner account status.\n\n**Authentication:** This endpoint requires a **Firebase ID token** (`Authorization: Bearer <firebase-token>`), not a partner API key. It is used by the partner portal dashboard on login.\n\nResponse varies by account state:\n- Active live key → returns `status: active`, `maskedKey`, `businessName`, `email`\n\n**`maskedKey` is for display only — it is not a usable credential.** API keys are stored as SHA-256 hashes, so the raw key is shown exactly once (at issue or rotation) and cannot be retrieved afterwards. To call the API from a signed-in portal session, exchange the Firebase ID token for a short-lived access token via `POST /api/v1/partner/session`.\n- Suspended key → returns `status: suspended`, `businessName`\n- Pending/rejected application (no live key yet) → returns `status`, `businessName`, `applicationSubmittedAt`, `applicationRejectionReason`\n- No application → returns `status: not_found`\n",
        "security": [
          {
            "FirebaseToken": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "TOKEN=$(firebase-auth-token)\ncurl https://mystocks.africa/api/v1/partner/me \\\n  -H \"Authorization: Bearer $TOKEN\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const token = await firebaseUser.getIdToken();\nconst res = await fetch('https://mystocks.africa/api/v1/partner/me', {\n  headers: { Authorization: `Bearer ${token}` },\n});\nconst { status, maskedKey, businessName } = await res.json();\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Partner account status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "suspended",
                        "pending",
                        "approved",
                        "rejected",
                        "sandbox_active",
                        "not_found"
                      ]
                    },
                    "sandboxActive": {
                      "type": "boolean",
                      "description": "True when this Firebase user has an active isolated sandbox identity."
                    },
                    "maskedKey": {
                      "type": "string",
                      "description": "Display-only mask of the partner's live key (e.g. `pk_live_a1b2c3d4...9f2e`). Present when status is active. NOT a usable credential — sending it as a Bearer token returns 401. Use POST /api/v1/partner/session for a credential the portal can actually call with."
                    },
                    "businessName": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "applicationSubmittedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When the partner application was submitted. Present for pending/approved/rejected states."
                    },
                    "applicationRejectionReason": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Admin-provided rejection reason. Present when status is rejected."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listMe"
      }
    },
    "/session": {
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Mint a portal session token",
        "x-sandbox": false,
        "description": "Exchanges a **Firebase ID token** (the partner's portal login) for a short-lived\n`ms_oauth_` access token that can be sent as `Authorization: Bearer <token>` to any\npartner endpoint.\n\nThis exists because partner API keys are stored only as SHA-256 hashes: the raw\n`pk_live_` key is displayed once, at issue or rotation, and is unrecoverable afterwards.\n`GET /me` can therefore only return a mask. A signed-in portal session uses this endpoint\nto obtain a credential it can actually call with.\n\nThe token carries the active organization member and mirrors the underlying key's scopes and\ntype. Effective access is the intersection of member permissions, key scopes, organization\nsecurity policy, and environment policy. It expires after 1 hour and can be revoked earlier.\n\nServer-to-server integrations should keep using their `pk_live_` key directly, or the\nOAuth client-credentials flow (`POST /partner/oauth/token`) — not this endpoint.\n",
        "security": [
          {
            "FirebaseToken": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const idToken = await firebaseUser.getIdToken();\nconst res = await fetch('https://mystocks.africa/api/v1/partner/session', {\n  method: 'POST',\n  headers: { Authorization: `Bearer ${idToken}` },\n});\nconst { accessToken, expiresIn } = await res.json();\n\n// Now call the partner API as the partner\nawait fetch('https://mystocks.africa/api/v1/partner/account', {\n  headers: { Authorization: `Bearer ${accessToken}` },\n});\n"
          }
        ],
        "responses": {
          "200": {
            "description": "A short-lived partner access token.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accessToken": {
                      "type": "string",
                      "description": "Send as `Authorization: Bearer <accessToken>`. Prefixed `ms_oauth_`."
                    },
                    "tokenType": {
                      "type": "string",
                      "enum": [
                        "Bearer"
                      ]
                    },
                    "expiresIn": {
                      "type": "integer",
                      "description": "Lifetime in seconds (3600)."
                    },
                    "keyType": {
                      "type": "string",
                      "enum": [
                        "full",
                        "data"
                      ],
                      "description": "Mirrors the underlying key. `data` tokens are read-only market-data credentials."
                    },
                    "member": {
                      "$ref": "#/components/schemas/PartnerMember"
                    },
                    "permissions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "sessionId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Firebase ID token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "No partner key provisioned for this account, or the key is suspended/revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createPartnerSession"
      }
    },
    "/reset": {
      "servers": [
        {
          "url": "https://mystocks.africa/api/sandbox/v1",
          "description": "Sandbox (reset endpoint lives outside the /partner/ namespace)"
        }
      ],
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Reset sandbox account",
        "description": "**Sandbox only.** Wipes all sub-accounts, orders, wallet balances, and webhooks for your partner key.\nUseful when you want a clean slate for a new test run.\nThis action is **irreversible** within a session — there is no undo.\n",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/sandbox/v1/reset \\\n  -H \"Authorization: Bearer sk_sandbox_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/sandbox/v1/reset', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer sk_sandbox_KEY' },\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/sandbox/v1/reset',\n  headers={'Authorization': 'Bearer sk_sandbox_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Account wiped.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Sandbox account reset successfully."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not allowed on production keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createReset"
      }
    },
    "/upgrade-request": {
      "get": {
        "tags": [
          "Registration"
        ],
        "summary": "Check upgrade request status",
        "description": "Returns the status of the caller's most recent upgrade request.\n**Authentication:** Firebase ID token required (`Authorization: Bearer <firebase-token>`).\nPass `?type=tier_upgrade` to check a tier upgrade request specifically.\n",
        "security": [
          {
            "FirebaseToken": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Filter to a specific request type. Omit for the most recent request of any type.",
            "schema": {
              "type": "string",
              "enum": [
                "live_key",
                "tier_upgrade"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Upgrade request status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requestId": {
                      "type": "string"
                    },
                    "type": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "enum": [
                        "live_key",
                        "tier_upgrade",
                        "null"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "pending",
                        "approved",
                        "rejected"
                      ]
                    },
                    "targetTier": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Present for tier_upgrade requests."
                    },
                    "currentTier": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "createdAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "reviewedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "reviewNote": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getUpgradeRequest"
      },
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Submit upgrade request",
        "description": "Submits an upgrade request to the MyStocks partnerships team.\n**Authentication:** Firebase ID token required (`Authorization: Bearer <firebase-token>`).\n\n**Two modes:**\n- **Sandbox → live key** (default, no `targetTier`): for partners without a live key yet. Triggers KYB review; live `pk_live_` key issued within 1–2 business days.\n- **Tier upgrade** (`targetTier` required): for partners with an active live key who want to move from starter → growth or enterprise.\n",
        "security": [
          {
            "FirebaseToken": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "TOKEN=$(firebase-auth-token)\n# Request live key\ncurl -X POST https://mystocks.africa/api/v1/partner/upgrade-request \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"message\":\"Integration complete, ready for live clients.\"}'\n# Request tier upgrade\ncurl -X POST https://mystocks.africa/api/v1/partner/upgrade-request \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"targetTier\":\"growth\",\"message\":\"Expecting 300 req/min at launch.\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const token = await firebaseUser.getIdToken();\nawait fetch('https://mystocks.africa/api/v1/partner/upgrade-request', {\n  method: 'POST',\n  headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ targetTier: 'growth', message: 'Need higher rate limit.' }),\n});\n"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "Optional message to the reviewing team."
                  },
                  "targetTier": {
                    "type": "string",
                    "enum": [
                      "growth",
                      "enterprise"
                    ],
                    "description": "Required for tier upgrade requests. Omit for sandbox-to-live requests."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Request submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "requestId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid targetTier or already on that tier."
          }
        },
        "operationId": "createUpgradeRequest"
      }
    },
    "/assets": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Unified asset catalog",
        "description": "Returns stocks, ETFs, bonds, and funds in one normalized catalog. Instruments that exist but cannot currently be transacted remain discoverable with `tradable: false`, a status, and capability flags. Unknown exchange order constraints are returned as null rather than inferred.",
        "parameters": [
          {
            "name": "assetClass",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "STOCK",
                "ETF",
                "BOND",
                "FUND"
              ]
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tradable",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Normalized asset page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "count",
                    "hasMore"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Asset"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listAssets"
      }
    },
    "/etfs": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "List ETFs",
        "description": "Returns a cursor-paginated list of tradeable ETFs across supported African exchanges.\nPrices are updated periodically during market hours.\nFilter by exchange or perform a text search on the ticker or name.\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Filter by exchange code. One of: NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM.",
            "schema": {
              "type": "string",
              "example": "JSE"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Search term matching the ETF ticker symbol or name.",
            "schema": {
              "type": "string",
              "example": "Gold"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor returned by the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/etfs?exchange=JSE\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/etfs?exchange=JSE',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { etfs, count } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/etfs',\n  params={'exchange': 'JSE'},\n  headers={'x-api-key': 'pk_live_KEY'})\netfs = r.json()['etfs']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "List of ETFs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "etfs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EtfSummary"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listEtfs"
      }
    },
    "/etfs/{symbol}": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Get ETF detail",
        "description": "Returns full price data, historical range chart data, and rich fund-specific metadata for a single ETF. Accepts exchange-qualified symbols (e.g., `GLD.ZA`) or bare tickers (e.g., `GLD`). Gated strictly to ETFs; querying standard stocks returns 404.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "description": "Exchange-qualified ETF symbol (e.g. `GLD.ZA`) or bare ticker.",
            "schema": {
              "type": "string",
              "example": "GLD.ZA"
            }
          },
          {
            "name": "range",
            "in": "query",
            "description": "Time range of legacy price history points to include. Default is `3M`.",
            "schema": {
              "type": "string",
              "enum": [
                "1W",
                "1M",
                "3M",
                "6M",
                "1Y",
                "ALL"
              ],
              "default": "3M"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/etfs/GLD.ZA?range=3M\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/etfs/GLD.ZA?range=3M', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst etf = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/etfs/GLD.ZA',\n  params={'range': '3M'},\n  headers={'x-api-key': 'pk_live_KEY'})\netf = r.json()\n"
          }
        ],
        "responses": {
          "200": {
            "description": "ETF details and historical price series.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/EtfSummary"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "currentPrice": {
                          "type": "number",
                          "example": 22000
                        },
                        "range": {
                          "type": "string",
                          "example": "3M"
                        },
                        "priceHistory": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "price": {
                                "type": "number"
                              },
                              "label": {
                                "type": "string",
                                "example": "Live"
                              }
                            }
                          }
                        },
                        "count": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found or security is not an ETF.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getEtf"
      }
    },
    "/etfs/{symbol}/history": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "ETF price history (candles)",
        "description": "Returns OHLCV candles for the given ETF symbol. Gated strictly to ETFs; querying standard stocks returns 404.\nSupported periods: `1d`, `1w`, `1m`, `3m`, `6m`, `1y`, `3y`, `5y`, `max`.\nCandle interval is auto-selected based on period.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GLD.ZA"
            }
          },
          {
            "name": "period",
            "in": "query",
            "description": "Time range. Default: 1m.",
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "1w",
                "1m",
                "3m",
                "6m",
                "1y",
                "3y",
                "5y",
                "max"
              ],
              "default": "1m"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/etfs/GLD.ZA/history?period=3m\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/etfs/GLD.ZA/history?period=3m',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { candles } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/etfs/GLD.ZA/history',\n  params={'period': '3m'}, headers={'x-api-key': 'pk_live_KEY'})\ncandles = r.json()['candles']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Price history candles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "candles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "open": {
                            "type": "number"
                          },
                          "high": {
                            "type": "number"
                          },
                          "low": {
                            "type": "number"
                          },
                          "close": {
                            "type": "number"
                          },
                          "volume": {
                            "type": "integer"
                          },
                          "ohlcAvailable": {
                            "type": "boolean"
                          },
                          "volumeAvailable": {
                            "type": "boolean"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found or security is not an ETF.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getEtfHistory"
      }
    },
    "/etfs/{symbol}/chart": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "ETF price chart",
        "description": "Returns pre-computed chart data for rendering price charts. Equivalent to `/etfs/{symbol}/history` but shaped for direct chart rendering with primary prices in local currency. Gated strictly to ETFs; querying standard stocks returns 404.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "GLD.ZA"
            }
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "1w",
                "1m",
                "3m",
                "6m",
                "1y",
                "3y",
                "5y",
                "max"
              ],
              "default": "1m"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/etfs/GLD.ZA/chart?period=1y\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/etfs/GLD.ZA/chart?period=1y',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { labels, prices } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/etfs/GLD.ZA/chart',\n  params={'period': '1y'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Chart data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "exchange": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "range": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "currentPrice": {
                      "type": "number"
                    },
                    "rangeChange": {
                      "type": "number"
                    },
                    "rangeChangePct": {
                      "type": "number"
                    },
                    "priceHistory": {
                      "type": "array",
                      "description": "Per-candle OHLCV objects, same order as labels.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "open": {
                            "type": "number",
                            "description": "Session open. Falls back to close for records pre-dating OHLCV storage."
                          },
                          "high": {
                            "type": "number",
                            "description": "Session high."
                          },
                          "low": {
                            "type": "number",
                            "description": "Session low."
                          },
                          "price": {
                            "type": "number",
                            "description": "Closing price (alias of close, retained for backward compatibility)."
                          },
                          "close": {
                            "type": "number",
                            "description": "Session closing price in local currency."
                          },
                          "usdPrice": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Closing price converted to USD at live spot rate."
                          },
                          "volume": {
                            "type": "integer",
                            "description": "Session trading volume."
                          }
                        }
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "opens": {
                      "type": "array",
                      "description": "Parallel array of open prices (same order as labels).",
                      "items": {
                        "type": "number"
                      }
                    },
                    "highs": {
                      "type": "array",
                      "description": "Parallel array of session highs.",
                      "items": {
                        "type": "number"
                      }
                    },
                    "lows": {
                      "type": "array",
                      "description": "Parallel array of session lows.",
                      "items": {
                        "type": "number"
                      }
                    },
                    "prices": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found or security is not an ETF.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getEtfChart"
      }
    },
    "/market/quotes": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Get quotes — single symbol or batch (NSE, NGX, JSE, GSE, etc.)",
        "description": "Returns delayed price quotes for one or many stock/ETF symbols. Operates in two modes:\n\n**Single-symbol mode** (`?symbol=X&exchange=Y`)\nBoth `symbol` and `exchange` are required. Returns a single `data` object.\n\n**Batch mode** (`?symbols=X,Y,Z`)\nComma-separated list of exchange-qualified tickers (up to 50). `exchange` is optional as a filter.\nReturns `data` array and a `not_found` array for any symbols that could not be resolved.\n\nAlso available at `GET /market-data/quotes` (identical handler).\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "description": "Single ticker symbol — exchange-qualified (e.g. `SCOM.KE`) or bare. Requires `exchange`. Use this or `symbols`, not both.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "symbols",
            "in": "query",
            "description": "Comma-separated batch of up to 50 exchange-qualified ticker symbols (e.g. `SCOM.KE,GLD.ZA,BAT.KE`). Use this or `symbol`, not both.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE,GLD.ZA"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "description": "Exchange code (NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM). Required in single-symbol mode; optional filter in batch mode.",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "# Single-symbol\ncurl \"https://mystocks.africa/api/v1/partner/market/quotes?symbol=SCOM.KE&exchange=NSE\" \\\n  -H \"x-api-key: pk_live_KEY\"\n# Batch\ncurl \"https://mystocks.africa/api/v1/partner/market/quotes?symbols=SCOM.KE,GLD.ZA\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "// Single-symbol\nconst res = await fetch(\n  'https://mystocks.africa/api/v1/partner/market/quotes?symbol=SCOM.KE&exchange=NSE',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data, meta } = await res.json();\n// Batch\nconst batchRes = await fetch(\n  'https://mystocks.africa/api/v1/partner/market/quotes?symbols=SCOM.KE,GLD.ZA',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data: quotes, not_found, meta: batchMeta } = await batchRes.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n# Single-symbol\nr = requests.get('https://mystocks.africa/api/v1/partner/market/quotes',\n  params={'symbol': 'SCOM.KE', 'exchange': 'NSE'},\n  headers={'x-api-key': 'pk_live_KEY'})\nstock = r.json()['data']\n# Batch\nr = requests.get('https://mystocks.africa/api/v1/partner/market/quotes',\n  params={'symbols': 'SCOM.KE,GLD.ZA'},\n  headers={'x-api-key': 'pk_live_KEY'})\nquotes = r.json()['data']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Quote response. Shape varies by mode:\n- **Single-symbol**: `data` is a single StockSummary object.\n- **Batch**: `data` is an array of StockSummary objects; `not_found` lists unresolvable symbols.\nBoth modes include a `meta` block with `request_id` and `timestamp`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "title": "Single-symbol response",
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/StockSummary"
                        },
                        "meta": {
                          "$ref": "#/components/schemas/Meta"
                        }
                      }
                    },
                    {
                      "title": "Batch response",
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/StockSummary"
                          }
                        },
                        "not_found": {
                          "type": "array",
                          "description": "Symbols that could not be resolved (unknown ticker or exchange mismatch).",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "UNKNOWN.NG"
                          ]
                        },
                        "meta": {
                          "$ref": "#/components/schemas/Meta"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error (missing required params, invalid symbol format, batch limit exceeded).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found (single-symbol mode only).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getBatchQuotes"
      }
    },
    "/stocks": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "List stocks (NSE, NGX, JSE, GSE, BRVM, etc.)",
        "description": "Returns the full tradeable stock and ETF universe across all supported African exchanges. \nProvides exchange-supplied, 15-minute-delayed stock price data. The API refresh target is 900 seconds during the published coverage window. These are not real-time prices. MyStocks holds full data rights for every supported exchange.\n- Nairobi Stock Exchange (NSE) in Kenya (KES)\n- Nigerian Exchange Group (NGX) in Nigeria (NGN)\n- Johannesburg Stock Exchange (JSE) in South Africa (ZAR)\n- Ghana Stock Exchange (GSE) in Ghana (GHS)\n- BRVM serving 8 West African French-speaking countries (XOF)\n- LuSE (Zambia), USE (Uganda), BSE (Botswana), DSE (Tanzania), ZSE (Zimbabwe), EGX (Egypt), MSE (Malawi), CSE (Morocco), and SEM (Mauritius).\n\nFilter by exchange, sector, asset type, or free-text `search`. Results are cursor-paginated to keep catalogue responses fast as market coverage grows. Use `nextCursor` while `hasMore` is true.\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Filter by exchange code. One of: NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM.",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "sector",
            "in": "query",
            "description": "Filter by GICS sector name.",
            "schema": {
              "type": "string",
              "example": "Telecommunications"
            }
          },
          {
            "name": "assetType",
            "in": "query",
            "description": "Filter to stocks or ETFs.",
            "schema": {
              "type": "string",
              "enum": [
                "STOCK",
                "ETF"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Free-text match against symbol or company name.",
            "schema": {
              "type": "string",
              "example": "safaricom"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size. Defaults to 50 and is capped at 200.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Continuation cursor returned as `nextCursor` by the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/stocks?exchange=NSE\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/stocks?exchange=NSE',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { stocks, count } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/stocks',\n  params={'exchange': 'NSE'},\n  headers={'x-api-key': 'pk_live_KEY'})\nstocks = r.json()['stocks']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "List of stocks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stocks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StockSummary"
                      }
                    },
                    "count": {
                      "type": "integer",
                      "description": "Number of stocks in this page."
                    },
                    "totalCount": {
                      "type": "integer",
                      "description": "Number of stocks matching the filters."
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listStocks"
      }
    },
    "/stocks/{symbol}": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Get stock detail (NSE, NGX, JSE, etc.)",
        "description": "Returns full delayed price and detail data for a single stock. Accepts exchange-qualified symbols (e.g. `SCOM.KE` for Safaricom PLC on the Nairobi Stock Exchange, `DANGCEM.NG` for Dangote Cement on the Nigerian Exchange Group, `MTN.ZA` on the Johannesburg Stock Exchange) or bare tickers (`SCOM`) when unambiguous.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "description": "Exchange-qualified ticker (e.g. `SCOM.KE`, `DANGCEM.NG`) or bare ticker.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/stocks/SCOM.KE \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/stocks/SCOM.KE', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst stock = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/stocks/SCOM.KE',\n  headers={'x-api-key': 'pk_live_KEY'})\nstock = r.json()\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Stock detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StockSummary"
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getStocks"
      }
    },
    "/stocks/{symbol}/history": {
      "get": {
        "deprecated": true,
        "tags": [
          "Market Data"
        ],
        "summary": "Stock price history",
        "description": "Legacy period-based history for the given symbol. Supported periods: `1d`, `1w`, `1m`, `3m`, `6m`,\n`1y`, `3y`, `5y`, and `max`. This endpoint returns available daily records; it does not aggregate weekly\nor monthly bars. New chart integrations should use `GET /stocks/{symbol}/candles`, which has explicit date\nbounds, aggregation intervals, data-quality metadata, and a stable candle schema. This route is deprecated\nand scheduled for removal on 2026-10-01.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "period",
            "in": "query",
            "description": "Time range. Default: 1m.",
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "1w",
                "1m",
                "3m",
                "6m",
                "1y",
                "3y",
                "5y",
                "max"
              ],
              "default": "1m"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/history?period=3m\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/history?period=3m',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { candles } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/history',\n  params={'period': '3m'}, headers={'x-api-key': 'pk_live_KEY'})\ncandles = r.json()['candles']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Price history candles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "candles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "open": {
                            "type": "number"
                          },
                          "high": {
                            "type": "number"
                          },
                          "low": {
                            "type": "number"
                          },
                          "close": {
                            "type": "number"
                          },
                          "volume": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listHistory"
      }
    },
    "/stocks/{symbol}/candles": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Historical stock candles",
        "description": "Returns chart-ready OHLCV candles in the listing currency, sorted oldest-first. `from` and `to` define an\ninclusive date window; `interval` selects intraday bucket sizes or daily/weekly/monthly aggregation.\n\nAfrican equity observations are exchange-supplied and 15-minute delayed. The API refresh target is 900 seconds\nduring the published coverage window. This is pull-based market data—not a real-time or tick feed. Every listed\nAfrican equity has EOD data for a line chart; complete reported OHLCV is instrument-dependent. Intraday candles are built only from observations\ncaptured after the intraday rollout; missing observations are never interpolated. The current delayed capture\ncan be materially coarser than the requested `15m` or `1h` bucket, so inspect `SPARSE_INTRADAY_COVERAGE`,\n`samplingCadenceSeconds`, and `intradayObservationCount` before rendering an intraday chart. Older daily source rows may\ncontain only close, or omit volume. Use each candle's `volumeAvailable` and the series coverage metadata before\nenabling volume-dependent indicators. Prices are raw and unadjusted for splits and dividends.\n\nMyStocks holds market-data rights for every supported exchange, so endpoint availability is not gated by\nmissing exchange licences. Authenticated partners may display these candles to their end-users under their\nPartner Agreement. Raw-feed redistribution, bulk export, and standalone data products remain governed by\nthat agreement and applicable downstream-use terms.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "description": "Exchange-qualified ticker (recommended) or an unambiguous bare ticker.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "interval",
            "in": "query",
            "description": "Candle bucket interval. `15m` and `1h` never interpolate missing observations and may be sparse on the delayed capture; `1mo` means one calendar month. `1m` is not supported.",
            "schema": {
              "type": "string",
              "enum": [
                "15m",
                "1h",
                "1d",
                "1w",
                "1mo"
              ],
              "default": "1d"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Inclusive start date. Defaults to 30 calendar days ago.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-01-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Inclusive end date. Defaults to today.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-08-10"
            }
          },
          {
            "name": "adjustment",
            "in": "query",
            "description": "Price-adjustment mode. Only raw, unadjusted exchange prices are currently available.",
            "schema": {
              "type": "string",
              "enum": [
                "raw"
              ],
              "default": "raw"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/candles?interval=1d&from=2026-01-01&to=2026-08-10\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/candles?interval=1d&from=2026-01-01&to=2026-08-10',\n  { headers: { 'x-api-key': 'pk_live_KEY' } },\n);\nconst { candles, meta } = await response.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nresponse = requests.get(\n  'https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/candles',\n  params={'interval': '1d', 'from': '2026-01-01', 'to': '2026-08-10'},\n  headers={'x-api-key': 'pk_live_KEY'},\n)\ncandles = response.json()['candles']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Historical candles and data-quality metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "symbol",
                    "name",
                    "exchange",
                    "currency",
                    "interval",
                    "adjustment",
                    "candles",
                    "meta"
                  ],
                  "properties": {
                    "symbol": {
                      "type": "string",
                      "example": "SCOM.KE"
                    },
                    "name": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "example": "Safaricom PLC"
                    },
                    "exchange": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "example": "NSE"
                    },
                    "currency": {
                      "type": "string",
                      "example": "KES"
                    },
                    "interval": {
                      "type": "string",
                      "enum": [
                        "15m",
                        "1h",
                        "1d",
                        "1w",
                        "1mo"
                      ]
                    },
                    "adjustment": {
                      "type": "string",
                      "enum": [
                        "raw"
                      ]
                    },
                    "candles": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Candle"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Meta"
                        },
                        {
                          "type": "object",
                          "required": [
                            "from",
                            "to",
                            "count",
                            "source",
                            "asOf",
                            "ohlcQuality",
                            "ohlcAvailable",
                            "ohlcCoveragePct",
                            "flatCandlePct",
                            "volumeAvailable",
                            "volumeCoveragePct",
                            "volumeStatus",
                            "zeroVolumePct",
                            "qualityStatus",
                            "qualityIssues",
                            "recommendedChartType",
                            "adjusted"
                          ],
                          "properties": {
                            "from": {
                              "type": "string",
                              "format": "date"
                            },
                            "to": {
                              "type": "string",
                              "format": "date"
                            },
                            "count": {
                              "type": "integer"
                            },
                            "source": {
                              "type": "string",
                              "enum": [
                                "delayed_eod",
                                "delayed_intraday"
                              ]
                            },
                            "asOf": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date-time",
                              "description": "Timestamp of the latest returned candle, or null when the range is empty."
                            },
                            "ohlcQuality": {
                              "type": "string",
                              "enum": [
                                "reported",
                                "mixed",
                                "close_derived",
                                "sampled"
                              ]
                            },
                            "ohlcAvailable": {
                              "type": "boolean"
                            },
                            "ohlcCoveragePct": {
                              "type": "number",
                              "minimum": 0,
                              "maximum": 100
                            },
                            "flatCandlePct": {
                              "type": "number",
                              "minimum": 0,
                              "maximum": 100
                            },
                            "volumeAvailable": {
                              "type": "boolean"
                            },
                            "volumeCoveragePct": {
                              "type": "number",
                              "minimum": 0,
                              "maximum": 100
                            },
                            "volumeStatus": {
                              "type": "string",
                              "enum": [
                                "unavailable",
                                "partial",
                                "complete"
                              ]
                            },
                            "zeroVolumePct": {
                              "type": "number",
                              "minimum": 0,
                              "maximum": 100
                            },
                            "qualityStatus": {
                              "type": "string",
                              "enum": [
                                "complete",
                                "partial",
                                "degraded",
                                "unavailable"
                              ]
                            },
                            "qualityIssues": {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "enum": [
                                  "NO_CHART_DATA",
                                  "OHLC_UNAVAILABLE",
                                  "OHLC_PARTIAL",
                                  "FLAT_SERIES",
                                  "HIGH_FLAT_CANDLE_RATE",
                                  "VOLUME_UNAVAILABLE",
                                  "VOLUME_PARTIAL",
                                  "HIGH_ZERO_VOLUME_RATE",
                                  "SPARSE_INTRADAY_COVERAGE"
                                ]
                              }
                            },
                            "recommendedChartType": {
                              "type": "string",
                              "enum": [
                                "candlestick",
                                "line",
                                "none"
                              ],
                              "description": "Server recommendation derived from price quality. Any missing OHLC, a flat-candle rate of 25% or more, or sparse intraday observations produces `line`; incomplete volume alone does not prevent candlesticks."
                            },
                            "samplingCadenceSeconds": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "Median observed same-session gap between source snapshots, not a promised target cadence.",
                              "example": 14400
                            },
                            "intradayObservationCount": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "Source observations available for the requested intraday window before bucketing."
                            },
                            "coverageStartsAt": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date-time"
                            },
                            "adjusted": {
                              "type": "boolean",
                              "enum": [
                                false
                              ]
                            },
                            "sandbox": {
                              "type": "boolean"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid date, interval, adjustment, or interval-specific date range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found or ambiguous.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getStockCandles"
      }
    },
    "/stocks/{symbol}/chart": {
      "get": {
        "deprecated": true,
        "tags": [
          "Market Data"
        ],
        "summary": "Stock price chart",
        "description": "Deprecated compatibility shape for older chart UIs. Use `/stocks/{symbol}/candles` for all new integrations. Scheduled for removal on 2026-10-01.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "1w",
                "1m",
                "3m",
                "6m",
                "1y",
                "3y",
                "5y",
                "max"
              ],
              "default": "1m"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/chart?period=1y\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/chart?period=1y',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { labels, prices } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/chart',\n  params={'period': '1y'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Chart data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "opens": {
                      "type": "array",
                      "description": "Parallel array of open prices (same order as labels).",
                      "items": {
                        "type": "number"
                      }
                    },
                    "highs": {
                      "type": "array",
                      "description": "Parallel array of session highs.",
                      "items": {
                        "type": "number"
                      }
                    },
                    "lows": {
                      "type": "array",
                      "description": "Parallel array of session lows.",
                      "items": {
                        "type": "number"
                      }
                    },
                    "prices": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    },
                    "priceHistory": {
                      "type": "array",
                      "description": "Per-candle OHLCV objects, same order as labels.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "open": {
                            "type": "number",
                            "description": "Session open. Falls back to close for records pre-dating OHLCV storage."
                          },
                          "high": {
                            "type": "number",
                            "description": "Session high."
                          },
                          "low": {
                            "type": "number",
                            "description": "Session low."
                          },
                          "price": {
                            "type": "number",
                            "description": "Closing price (alias of close, retained for backward compatibility)."
                          },
                          "close": {
                            "type": "number",
                            "description": "Session closing price in local currency."
                          },
                          "usdPrice": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Closing price converted to USD at live spot rate."
                          },
                          "volume": {
                            "type": "integer",
                            "description": "Session trading volume."
                          }
                        }
                      }
                    },
                    "rangeChange": {
                      "type": "number"
                    },
                    "rangeChangePct": {
                      "type": "number"
                    },
                    "currentPrice": {
                      "type": "number"
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getStockChart"
      }
    },
    "/stocks/{symbol}/pulse": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Stock Pulse (news)",
        "description": "Returns recent news articles and exchange announcements for a specific stock. Sourced from African financial news networks and official exchange feeds.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 50
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/pulse?limit=5\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/pulse?limit=5',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { articles } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/stocks/SCOM.KE/pulse',\n  params={'limit': 5}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "News articles for this stock.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "articles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "title": {
                            "type": "string"
                          },
                          "summary": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "source": {
                            "type": "string"
                          },
                          "publishedAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "imageUrl": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "uri"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPulse"
      }
    },
    "/companies": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "List companies",
        "description": "Returns company profiles with fundamental data (description, sector, country, financials). Supports exchange and sector filtering.",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "JSE"
            }
          },
          {
            "name": "sector",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor returned by the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/companies?exchange=JSE&limit=10\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/companies?exchange=JSE',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { companies } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/companies',\n  params={'exchange': 'JSE'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Company list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "companies": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": "string"
                          },
                          "sector": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "country": {
                            "type": "string"
                          },
                          "description": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "logoUrl": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listCompanies"
      }
    },
    "/companies/tickers": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Ticker symbols",
        "description": "Returns a cursor-paginated ticker catalogue across exchanges. Useful for symbol autocomplete or validation.",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assetType",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor returned by the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/companies/tickers \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/companies/tickers', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { tickers } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/companies/tickers',\n  headers={'x-api-key': 'pk_live_KEY'})\ntickers = r.json()['tickers']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "All ticker symbols.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tickers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "SCOM.KE",
                        "EQTY.KE",
                        "DANGCEM.NG",
                        "NPN.ZA"
                      ]
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listTickers"
      }
    },
    "/companies/{symbol}": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Company profile",
        "description": "Returns detailed company profile including description, headquarters, sector, key financials, and links.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/companies/SCOM.KE \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/companies/SCOM.KE', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst company = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/companies/SCOM.KE',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Company profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "exchange": {
                      "type": "string"
                    },
                    "sector": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "country": {
                      "type": "string"
                    },
                    "description": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "website": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    },
                    "logoUrl": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "marketCap": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "peRatio": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Company not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getCompanies"
      }
    },
    "/companies/{symbol}/chart": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Price chart",
        "description": "Returns pre-computed chart data for rendering price charts. Equivalent to `/stocks/{symbol}/history` but shaped for direct chart rendering.",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "1w",
                "1m",
                "3m",
                "6m",
                "1y",
                "3y",
                "5y",
                "max"
              ],
              "default": "1m"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/companies/SCOM.KE/chart?period=1y\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/companies/SCOM.KE/chart?period=1y',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { labels, prices } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/companies/SCOM.KE/chart',\n  params={'period': '1y'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Chart data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "opens": {
                      "type": "array",
                      "description": "Parallel array of open prices (same order as labels).",
                      "items": {
                        "type": "number"
                      }
                    },
                    "highs": {
                      "type": "array",
                      "description": "Parallel array of session highs.",
                      "items": {
                        "type": "number"
                      }
                    },
                    "lows": {
                      "type": "array",
                      "description": "Parallel array of session lows.",
                      "items": {
                        "type": "number"
                      }
                    },
                    "prices": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    },
                    "priceHistory": {
                      "type": "array",
                      "description": "Per-candle OHLCV objects, same order as labels.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "open": {
                            "type": "number",
                            "description": "Session open. Falls back to close for records pre-dating OHLCV storage."
                          },
                          "high": {
                            "type": "number",
                            "description": "Session high."
                          },
                          "low": {
                            "type": "number",
                            "description": "Session low."
                          },
                          "price": {
                            "type": "number",
                            "description": "Closing price (alias of close, retained for backward compatibility)."
                          },
                          "close": {
                            "type": "number",
                            "description": "Session closing price in local currency."
                          },
                          "usdPrice": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Closing price converted to USD at live spot rate."
                          },
                          "volume": {
                            "type": "integer",
                            "description": "Session trading volume."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listChart"
      }
    },
    "/companies/{symbol}/news": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Company news & corporate actions",
        "description": "Returns a merged, reverse-chronological feed for a single company: MyStocks' curated market-intelligence items (`source: MARKET_NEWS`) plus corporate actions such as dividends, splits, and rights issues (`source: CORPORATE_ACTION`). This is the same curated editorial content shown in the MyStocks consumer app — not a high-volume third-party newswire. Filter to one kind with `?type=NEWS`.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Optional filter by item type, e.g. NEWS or a corporate-action type (DIVIDEND, SPLIT, ...).",
            "schema": {
              "type": "string",
              "example": "NEWS"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/companies/SCOM.KE/news?limit=5\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/companies/SCOM.KE/news?limit=5',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { items } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/companies/SCOM.KE/news',\n  params={'limit': 5}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Merged company news and corporate-action feed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string",
                      "example": "SCOM.KE"
                    },
                    "name": {
                      "type": "string",
                      "example": "Safaricom PLC"
                    },
                    "count": {
                      "type": "integer",
                      "example": 5
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "MARKET_NEWS",
                              "CORPORATE_ACTION"
                            ]
                          },
                          "type": {
                            "type": "string",
                            "description": "NEWS for market intel; the action type (e.g. DIVIDEND) for corporate actions.",
                            "example": "NEWS"
                          },
                          "title": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string",
                            "nullable": true
                          },
                          "url": {
                            "type": "string",
                            "format": "uri",
                            "nullable": true
                          },
                          "exDate": {
                            "type": "string",
                            "nullable": true,
                            "description": "Ex-date (corporate actions only)."
                          },
                          "payDate": {
                            "type": "string",
                            "nullable": true,
                            "description": "Pay-date (corporate actions only)."
                          },
                          "amount": {
                            "type": "number",
                            "nullable": true,
                            "description": "Distribution amount (corporate actions only)."
                          },
                          "currency": {
                            "type": "string",
                            "nullable": true
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listNews"
      }
    },
    "/market-intel": {
      "get": {
        "tags": [
          "Market Intelligence"
        ],
        "summary": "Market Intel feed",
        "description": "Returns curated editorial articles across all African exchanges — macro analysis, sector reports,\nearnings commentary, and exchange announcements. Updated throughout the trading day.\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Filter articles by exchange.",
            "schema": {
              "type": "string",
              "example": "NGX"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/market-intel?exchange=NGX&limit=10\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/market-intel?exchange=NGX',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { articles } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market-intel',\n  params={'exchange': 'NGX', 'limit': 10},\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Market intelligence articles.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "articles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "summary": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "source": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "category": {
                            "type": "string",
                            "enum": [
                              "macro",
                              "sector",
                              "earnings",
                              "announcement"
                            ]
                          },
                          "publishedAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "imageUrl": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listMarketIntel"
      }
    },
    "/market-intel/{id}": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Market intel article detail",
        "description": "Returns full detail for a single market intelligence article, including the full body. Resolves by Firestore document ID or slug.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Article document ID or URL slug.",
            "schema": {
              "type": "string",
              "example": "nse-q1-2026-outlook"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/market-intel/nse-q1-2026-outlook \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Article detail with full body.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "summary": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "body": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Full article HTML or markdown content."
                    },
                    "slug": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "category": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "symbols": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "exchange": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "imageUrl": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    },
                    "source": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "sourceUrl": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    },
                    "author": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "publishedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "createdAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Article not found."
          }
        },
        "operationId": "getMarketIntelArticle"
      }
    },
    "/market/status": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Market status (NSE, NGX, JSE, GSE, etc.)",
        "description": "Returns the real-time open/closed status for each supported African stock exchange (NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM), next session boundaries, server time, and local trading hours.\n\nPass `?exchange=NSE` to get status for a single exchange. Without the filter the response includes all exchanges plus an `anyOpen` boolean.\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Filter to a single exchange code (NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM).",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/market/status \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/market/status', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { anyOpen, exchanges } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market/status',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Exchange status map.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "anyOpen": {
                      "type": "boolean",
                      "description": "True if at least one exchange is currently open."
                    },
                    "checkedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "serverTime": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "exchanges": {
                      "type": "object",
                      "description": "Keyed by exchange code (e.g. \"NSE\", \"NGX\"). Each value is an exchange status object.",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string",
                            "example": "Nairobi Securities Exchange"
                          },
                          "country": {
                            "type": "string",
                            "example": "Kenya"
                          },
                          "currency": {
                            "type": "string",
                            "example": "KES"
                          },
                          "isOpen": {
                            "type": "boolean"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "OPEN",
                              "CLOSED"
                            ]
                          },
                          "localOpen": {
                            "type": "string",
                            "description": "Local market open time (HH:MM).",
                            "example": "09:00"
                          },
                          "localClose": {
                            "type": "string",
                            "description": "Local market close time (HH:MM).",
                            "example": "15:00"
                          },
                          "timezone": {
                            "type": "string",
                            "example": "Africa/Nairobi"
                          },
                          "nextOpen": {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO timestamp of the next future session open, skipping known holidays."
                          },
                          "nextClose": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Current session close when open; otherwise the close of the next session."
                          },
                          "serverTime": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "reason": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "enum": [
                              "HOLIDAY",
                              "NON_TRADING_DAY",
                              "OUTSIDE_TRADING_HOURS",
                              null
                            ]
                          },
                          "session": {
                            "oneOf": [
                              {
                                "type": "object",
                                "required": [
                                  "open",
                                  "close"
                                ],
                                "properties": {
                                  "open": {
                                    "type": "string",
                                    "format": "date-time"
                                  },
                                  "close": {
                                    "type": "string",
                                    "format": "date-time"
                                  }
                                }
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown exchange code."
          }
        },
        "operationId": "getMarketStatus"
      }
    },
    "/market/clock": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Canonical exchange clock",
        "description": "Alias of `/market/status`. Returns the identical payload and uses the same holiday-aware session calculation.",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Optional supported exchange code.",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/paths/~1market~1status/get/responses/200"
          },
          "404": {
            "description": "Unknown exchange code."
          }
        },
        "operationId": "getMarketClock"
      }
    },
    "/market/settlement": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Settlement cycles and live fill target",
        "description": "Returns the official T+N settlement cycle for each supported exchange (i.e. when title and beneficial ownership transfer after trade execution), plus the MyStocks live fill SLA for market orders submitted during exchange hours.\n\n**Important distinction**\n- **MyStocks live fill SLA** — the time before a PENDING live order moves to COMPLETED or REJECTED. Execution window: 1 to 5 minutes during market hours.\n- **Exchange settlement cycle** — the standard T+N cycle mandated by the exchange and its CSD. This governs when the underlying shares clear through the depository (T+2 for EGX, T+3 for all other supported exchanges).\n\nUse `?exchange=NSE` to filter to a single exchange.\n",
        "operationId": "getMarketSettlement",
        "parameters": [
          {
            "in": "query",
            "name": "exchange",
            "schema": {
              "type": "string",
              "example": "NSE"
            },
            "description": "Filter to a single exchange code (NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM)."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/market/settlement \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/market/settlement', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { processingWindow, exchanges } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market/settlement',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Settlement cycles and live fill target.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "processingWindow": {
                      "type": "object",
                      "properties": {
                        "targetTurnaroundHours": {
                          "type": "number",
                          "example": 0.083,
                          "description": "Legacy hour equivalent of the live 5-minute fill target."
                        },
                        "minTurnaroundMinutes": {
                          "type": "integer",
                          "example": 1,
                          "description": "Earliest expected live execution window, in minutes."
                        },
                        "maxTurnaroundMinutes": {
                          "type": "integer",
                          "example": 5,
                          "description": "Maximum live execution window, in minutes."
                        },
                        "targetTurnaroundMinutes": {
                          "type": "integer",
                          "example": 5,
                          "description": "Monitoring deadline between live market order submission and fill/rejection during market hours."
                        },
                        "description": {
                          "type": "string"
                        },
                        "outsideHoursNote": {
                          "type": "string"
                        },
                        "processingDays": {
                          "type": "string",
                          "example": "Monday – Friday (excluding public holidays)"
                        },
                        "webhookNote": {
                          "type": "string"
                        },
                        "pollEndpoint": {
                          "type": "string",
                          "example": "GET /api/v1/partner/orders/{orderId}"
                        }
                      }
                    },
                    "exchanges": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "exchange": {
                            "type": "string",
                            "example": "NSE"
                          },
                          "name": {
                            "type": "string",
                            "example": "Nairobi Securities Exchange"
                          },
                          "country": {
                            "type": "string",
                            "example": "Kenya"
                          },
                          "currency": {
                            "type": "string",
                            "example": "KES"
                          },
                          "cycle": {
                            "type": "string",
                            "example": "T+3",
                            "description": "Official exchange settlement cycle."
                          },
                          "cycleDays": {
                            "type": "integer",
                            "example": 3
                          },
                          "notes": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          },
          "404": {
            "description": "Unknown exchange code."
          }
        }
      }
    },
    "/market/holidays": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Market holiday calendar",
        "description": "Returns exchange closure dates for a given date range. Use this to:\n- Show users when a specific exchange is closed before they try to trade.\n- Build \"next trading day\" schedulers (skip weekends and holidays).\n- Display an in-app holiday calendar alongside market status.\n\nDefaults: `from` = today, `to` = 12 months from today. Maximum range is 2 years.\nAll African exchanges (UTC+0 to UTC+3) use ISO 8601 dates in UTC.\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "NSE"
            },
            "description": "Filter to a single exchange. One of: NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM. Omit to return holidays for all exchanges."
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-06-06"
            },
            "description": "Start date (inclusive), YYYY-MM-DD. Defaults to today."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-12-31"
            },
            "description": "End date (inclusive), YYYY-MM-DD. Defaults to 12 months from from. Maximum 2 years from from."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "# All NSE holidays for the next 6 months\ncurl \"https://mystocks.africa/api/v1/partner/market/holidays?exchange=NSE&from=2026-06-06&to=2026-12-31\" \\\n  -H \"x-api-key: pk_live_KEY\"\n\n# All exchanges, full upcoming year (default range)\ncurl \"https://mystocks.africa/api/v1/partner/market/holidays\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "// NSE holidays — next 6 months\nconst res = await fetch(\n  'https://mystocks.africa/api/v1/partner/market/holidays?exchange=NSE&from=2026-06-06&to=2026-12-31',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data } = await res.json();\n// data: [{ exchange: 'NSE', date: '2026-06-01', reason: 'Madaraka Day' }, ...]\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get(\n  'https://mystocks.africa/api/v1/partner/market/holidays',\n  params={'exchange': 'NSE', 'from': '2026-06-06', 'to': '2026-12-31'},\n  headers={'x-api-key': 'pk_live_KEY'}\n)\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Holiday calendar entries for the requested range.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "exchange",
                          "date",
                          "reason"
                        ],
                        "properties": {
                          "exchange": {
                            "type": "string",
                            "example": "NSE",
                            "description": "Exchange code."
                          },
                          "date": {
                            "type": "string",
                            "format": "date",
                            "example": "2026-12-25",
                            "description": "Closure date in YYYY-MM-DD format."
                          },
                          "reason": {
                            "type": "string",
                            "example": "Christmas Day",
                            "description": "Human-readable reason for the closure."
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer",
                      "example": 8
                    },
                    "from": {
                      "type": "string",
                      "format": "date",
                      "example": "2026-06-06"
                    },
                    "to": {
                      "type": "string",
                      "format": "date",
                      "example": "2026-12-31"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid exchange code or date format.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        },
        "operationId": "getMarketHolidays"
      }
    },
    "/fx/rates": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Managed FX rates",
        "description": "Returns the MyStocks-managed FX table used to convert local market currencies and partner local funding instructions into the USD ledger. Rates use the convention \"local currency units per USD\".\n",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/fx/rates \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Managed FX rates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "baseCurrency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "ledgerCurrency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "source": {
                      "type": "string",
                      "example": "MYSTOCKS_MANAGED_FX"
                    },
                    "rateConvention": {
                      "type": "string",
                      "example": "local currency units per USD"
                    },
                    "asOf": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "supportedCurrencies": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "rates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "currency": {
                            "type": "string",
                            "example": "KES"
                          },
                          "rate": {
                            "type": "number",
                            "example": 130
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        },
        "operationId": "listFxRates"
      }
    },
    "/quote/{symbol}": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Pre-trade fee quote",
        "description": "Returns a pre-trade fee quote using the latest delayed price observation. Includes the local and USD price, gross order cost, base fee, partner markup fee, and total cost for BUY or estimated proceeds for SELL. Also returns whether the partner wallet has sufficient balance/holdings.\n\nSize the quote with **either** `quantity` (share mode) **or** `cashValue` (cash mode) — they are mutually exclusive, and the subsequent trade must use the **same mode and value**. Cash-mode quotes return an indicative fractional `quantity`; the order is sized at execution price and validated against the quoted `cashValue`.\nQuotes remain valid for 60 seconds to protect quote-to-fill price integrity. Mobile clients should display `quoteExpiresAt`, refresh when 10 seconds remain or whenever the app resumes, and require confirmation again if the refreshed cost, proceeds, fee, or quantity changes. Fetching a replacement quote does not place an order.\n\nAlways fetch a quote immediately before placing a trade to confirm the current cost.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "description": "Exchange-qualified ticker (e.g. SCOM.KE) or bare ticker.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Order direction. Defaults to BUY.",
            "schema": {
              "type": "string",
              "enum": [
                "BUY",
                "SELL"
              ],
              "default": "BUY"
            }
          },
          {
            "name": "quantity",
            "in": "query",
            "required": false,
            "description": "Number of shares to quote (fractional allowed). Mutually exclusive with cashValue. Defaults to 1 when neither is passed.",
            "schema": {
              "type": "number",
              "example": 500
            }
          },
          {
            "name": "cashValue",
            "in": "query",
            "required": false,
            "description": "USD amount to quote in cash mode (fractional investing). Mutually exclusive with quantity. The trade must then be placed with the same cashValue and this quoteId.",
            "schema": {
              "type": "number",
              "example": 50
            }
          },
          {
            "name": "subAccountId",
            "in": "query",
            "required": false,
            "description": "Optional sub-account ID when quoting a trade for /users/{userId}/trade. The account must exist under the authenticated partner key.",
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/quote/SCOM.KE?type=BUY&quantity=500\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/quote/SCOM.KE?type=BUY&quantity=500',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { totalCost, fee, sufficientFunds } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get(\n  'https://mystocks.africa/api/v1/partner/quote/SCOM.KE',\n  params={'type': 'BUY', 'quantity': 500},\n  headers={'x-api-key': 'pk_live_KEY'})\nquote = r.json()\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Fee quote for the requested symbol, type, and quantity.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "quoteId": {
                      "type": "string",
                      "description": "Quote ID to submit in the subsequent trade request."
                    },
                    "quoteTtlSeconds": {
                      "type": "integer",
                      "example": 60
                    },
                    "quoteIssuedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "quoteExpiresAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "sourceUpdatedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Timestamp of the source market price used for this quote."
                    },
                    "dataFreshnessSeconds": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Age in seconds of the source market price when the quote was issued."
                    },
                    "asOf": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Canonical timestamp of the upstream price observation; equivalent to sourceUpdatedAt."
                    },
                    "ageSeconds": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Canonical price age in seconds; equivalent to dataFreshnessSeconds."
                    },
                    "stale": {
                      "type": "boolean",
                      "description": "True when the price observation is older than two refresh intervals."
                    },
                    "delayMode": {
                      "type": "string",
                      "enum": [
                        "DELAYED",
                        "END_OF_DAY"
                      ]
                    },
                    "delayMinutes": {
                      "type": "integer",
                      "example": 15
                    },
                    "refreshIntervalSeconds": {
                      "type": "integer",
                      "example": 900
                    },
                    "provider": {
                      "type": "string",
                      "enum": [
                        "mstocks",
                        "mansa"
                      ]
                    },
                    "disclaimer": {
                      "type": "string",
                      "description": "Plain-English delayed-price warning."
                    },
                    "dataQuality": {
                      "$ref": "#/components/schemas/QuoteDataQuality"
                    },
                    "symbol": {
                      "type": "string",
                      "example": "SCOM.KE"
                    },
                    "name": {
                      "type": "string"
                    },
                    "exchange": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string",
                      "example": "KES"
                    },
                    "ledgerCurrency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "fxRate": {
                      "type": "number",
                      "description": "Managed FX rate used to convert local price to USD."
                    },
                    "fxSource": {
                      "type": "string",
                      "example": "MYSTOCKS_MANAGED_FX"
                    },
                    "fxRateAsOf": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "fxConversion": {
                      "$ref": "#/components/schemas/FxConversion"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "BUY",
                        "SELL"
                      ]
                    },
                    "quantity": {
                      "type": "integer"
                    },
                    "localPrice": {
                      "type": "number",
                      "description": "Current price in the stock's local currency."
                    },
                    "usdPrice": {
                      "type": "number",
                      "description": "Current price converted to USD."
                    },
                    "gross": {
                      "type": "number",
                      "description": "quantity × usdPrice."
                    },
                    "baseFee": {
                      "type": "number",
                      "description": "MyStocks base broker fee (0.75% of gross)."
                    },
                    "partnerMarkupFee": {
                      "type": "number",
                      "description": "Partner markup component of the fee. Zero if markupBps = 0."
                    },
                    "fee": {
                      "type": "number",
                      "description": "Total fee (baseFee + partnerMarkupFee)."
                    },
                    "feeRate": {
                      "type": "number",
                      "description": "Effective fee rate as a percentage (e.g. 0.75)."
                    },
                    "totalCost": {
                      "type": "number",
                      "description": "BUY only — gross + fee. The amount escrowed on order submission."
                    },
                    "estimatedProceeds": {
                      "type": "number",
                      "description": "SELL only — gross - fee. Estimated wallet credit on settlement."
                    },
                    "walletBalance": {
                      "type": "number",
                      "description": "Partner master wallet balance in USD."
                    },
                    "sufficientFunds": {
                      "type": "boolean",
                      "description": "BUY only — true if walletBalance >= totalCost."
                    },
                    "units": {
                      "type": "number",
                      "description": "SELL only — units of this stock currently held."
                    },
                    "sufficientHoldings": {
                      "type": "boolean",
                      "description": "SELL only — true if units >= quantity."
                    },
                    "note": {
                      "type": "string",
                      "example": "This is a quote only. No order has been placed."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Symbol or scoped sub-account not found. Unknown sub-accounts return `SUBACCOUNT_NOT_FOUND` and no quote is created."
          }
        },
        "operationId": "getQuote"
      }
    },
    "/trade": {
      "post": {
        "tags": [
          "Trading"
        ],
        "summary": "Place trade",
        "description": "Places a BUY or SELL order on your own partner account (not a sub-account — use `/users/{userId}/trade` for those).\n\n**BUY** — escrows USD from your master wallet. Settlement is T+3 for most African exchanges.\n**SELL** — requires you to hold the shares. Proceeds are credited on settlement.\n\nPass `Idempotency-Key` to make retries safe. The same key returns the cached order within 24 h.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TradeRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/trade \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: trade_$(date +%s)\" \\\n  -d '{\"symbol\":\"SCOM.KE\",\"type\":\"BUY\",\"quantity\":500}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/trade', {\n  method: 'POST',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Content-Type': 'application/json',\n    'Idempotency-Key': `trade_${Date.now()}`,\n  },\n  body: JSON.stringify({ symbol: 'SCOM.KE', type: 'BUY', quantity: 500 }),\n});\nconst { orderId, status } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests, time\nr = requests.post('https://mystocks.africa/api/v1/partner/trade',\n  headers={\n    'Authorization': 'Bearer pk_live_KEY',\n    'Idempotency-Key': f'trade_{int(time.time())}',\n  },\n  json={'symbol': 'SCOM.KE', 'type': 'BUY', 'quantity': 500})\norder = r.json()\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Order placed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "400": {
            "description": "Insufficient wallet balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict — duplicate in-flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Business rule violation (e.g. market closed, ambiguous symbol).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createTrade"
      }
    },
    "/portfolio": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Portfolio",
        "description": "Returns all open equity positions on your partner account — current holdings, average cost, unrealised P&L, and market value in USD.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/portfolio \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/portfolio', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { holdings, summary } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/portfolio',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Portfolio positions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortfolioResponse"
                }
              }
            }
          }
        },
        "operationId": "listPortfolio"
      }
    },
    "/portfolio/history": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Master-account equity history",
        "description": "Daily raw-equity history for the partner master trading account only. This endpoint does not consolidate sub-accounts and is not cash-flow adjusted; deposits and withdrawals therefore affect changes in equity.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1M",
                "3M",
                "6M",
                "1Y",
                "ALL"
              ],
              "default": "1Y"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Master-account daily equity points.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortfolioHistoryResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid period or date range."
          }
        },
        "operationId": "getPortfolioHistory"
      }
    },
    "/portfolio/performance": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Master-account cash-flow-adjusted performance",
        "description": "Returns daily P&L, Modified Dietz interval returns, and geometrically linked cumulative time-weighted return. Completed master/sub-account transfers and completed external top-ups/payouts are cash-flow adjusted. Supply an optional exchange-qualified benchmark symbol for rebased price return and excess-return comparison. Benchmark return excludes dividends and FX. Use /portfolio/history when raw equity snapshots are required.",
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1M",
                "3M",
                "6M",
                "1Y",
                "ALL"
              ],
              "default": "1Y"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "benchmark",
            "in": "query",
            "description": "Optional exchange-qualified benchmark symbol.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cash-flow-adjusted master-account performance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortfolioPerformanceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid period",
            "date range": null,
            "or benchmark format.": null
          },
          "404": {
            "description": "Benchmark symbol not found."
          }
        },
        "operationId": "getPortfolioPerformance"
      }
    },
    "/orders": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "List orders",
        "description": "Returns your partner order history. Filter by status, symbol, or date range.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "WORKING",
                "PARTIALLY_FILLED",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED",
                "EXPIRED"
              ],
              "description": "FILLED is canonical. COMPLETED and PROCESSING are legacy compatibility aliases."
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "ISO 8601 date — return orders created on or after this date.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-01-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-03-31"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from `nextCursor` of the previous response. Omit for the first page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/orders?status=FILLED&limit=20\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/orders?status=FILLED',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { orders, total } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/orders',\n  params={'status': 'FILLED', 'limit': 20},\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listOrders"
      }
    },
    "/orders/{orderId}": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Get order",
        "description": "Returns a single order by ID.",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst order = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "404": {
            "description": "Order not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getOrder"
      },
      "delete": {
        "tags": [
          "Trading"
        ],
        "summary": "Cancel order",
        "description": "Cancels a PENDING order. Returns 422 if the order has already been settled or rejected.",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: cancel_order_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz', {\n  method: 'DELETE',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'cancel_order_001' },\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.delete('https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'cancel_order_001'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order cancelled. BUY escrow refunded atomically.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orderId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "CANCELLED"
                      ]
                    },
                    "refunded": {
                      "type": "number",
                      "description": "USD amount returned to master wallet. Present for BUY orders only."
                    },
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Order cannot be cancelled — it is no longer PENDING or WORKING (already PROCESSING, COMPLETED, or REJECTED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteOrders"
      },
      "patch": {
        "x-sandbox": false,
        "x-sandbox-stub": true,
        "tags": [
          "Trading"
        ],
        "summary": "Modify (replace) a resting order",
        "description": "Replaces a resting LIMIT/STOP/STOP_LIMIT order on the master account in place, keeping the same\norderId. Only WORKING orders can be modified — a MARKET order is already PENDING at the dealing\ndesk and can only be cancelled. Check `replaceable` on the order.\n\nSupply any combination of limitPrice, stopPrice, and quantity. The replace is repriced on the\nsame FX basis and fee rates captured when the order was placed. A BUY re-escrows the difference\n(400 INSUFFICIENT_FUNDS if the wallet cannot cover an increase); a SELL adjusts reserved units.\nAn Idempotency-Key is required because this moves money.\n\nThe sub-account equivalent is PATCH /users/{userId}/orders/{orderId}.\n",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "minProperties": 1,
                "properties": {
                  "limitPrice": {
                    "type": "number",
                    "description": "New limit price, in the instrument's local trading currency.",
                    "example": 18.5
                  },
                  "stopPrice": {
                    "type": "number",
                    "description": "New stop price, in the instrument's local trading currency.",
                    "example": 17.25
                  },
                  "quantity": {
                    "type": "number",
                    "description": "New quantity. Converts a cashValue-sized order into a quantity-sized one.",
                    "example": 200
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PATCH https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: 8f14e45f-ceea-467a-9575-9f0c0b6f1f2a\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"limitPrice\": 18.50, \"quantity\": 200}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz', {\n  method: 'PATCH',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Idempotency-Key': crypto.randomUUID(),\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({ limitPrice: 18.5, quantity: 200 }),\n});\nconst order = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests, uuid\nr = requests.patch('https://mystocks.africa/api/v1/partner/orders/ord_abc123xyz',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': str(uuid.uuid4())},\n  json={'limitPrice': 18.50, 'quantity': 200})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order modified. Escrow / unit reservation adjusted atomically.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, or insufficient funds to increase a BUY escrow.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Order is not WORKING, or is not a resting order type, so it cannot be modified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "patchOrder"
      }
    },
    "/orders/{orderId}/executions": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Trading"
        ],
        "summary": "Get order execution reports",
        "description": "Returns OMS execution reports for a partner master-account order, including receipt, fill, rejection, and cancellation reports.",
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Execution reports for the order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecutionReportsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Order not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getOrderExecutions"
      }
    },
    "/bonds": {
      "get": {
        "tags": [
          "Asset Classes"
        ],
        "summary": "List bonds & fixed income",
        "description": "Returns all available bonds, treasury bills, and fixed-income instruments. Includes coupon rate, maturity date, minimum investment, and current yield.",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "description": "Filter by country code.",
            "schema": {
              "type": "string",
              "example": "KE"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Instrument type filter.",
            "schema": {
              "type": "string",
              "enum": [
                "BOND",
                "TBILL",
                "CORPORATE"
              ]
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/bonds?country=KE\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/bonds?country=KE', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { bonds } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/bonds',\n  params={'country': 'KE'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Bond list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bonds": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "description": "Current catalogue availability state. Only ACTIVE instruments accept new subscriptions."
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "couponRate": {
                            "type": "number",
                            "description": "Annual coupon rate as a percentage."
                          },
                          "currentYield": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Computed current yield (couponRate × faceValue / pricePerUnit). Null for zero-coupon/discount instruments."
                          },
                          "yieldToMaturity": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Expected annualised return if held to maturity. Admin-entered; null if not provided."
                          },
                          "modifiedDuration": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Modified duration in years — measures price sensitivity to a 1% yield change."
                          },
                          "creditRating": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Credit rating assigned by a rating agency (e.g. AAA, BBB+).",
                            "example": "BBB+"
                          },
                          "creditRatingAgency": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Rating agency that issued the credit rating (e.g. S&P, GCR Africa).",
                            "example": "GCR Africa"
                          },
                          "maturityDate": {
                            "type": "string",
                            "format": "date"
                          },
                          "faceValue": {
                            "type": "number"
                          },
                          "minInvestment": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listBonds"
      }
    },
    "/bonds/{id}": {
      "get": {
        "tags": [
          "Asset Classes"
        ],
        "summary": "Bond detail",
        "description": "Returns full detail for a specific bond or fixed-income instrument.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Bond document ID from the list endpoint.",
            "schema": {
              "type": "string",
              "example": "ke_tbill_91d_2026"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/bonds/ke_tbill_91d_2026 \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/bonds/ke_tbill_91d_2026', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/bonds/ke_tbill_91d_2026',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Bond detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "description": "Current catalogue availability state. Only ACTIVE instruments accept new subscriptions."
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "type": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string"
                    },
                    "couponRate": {
                      "type": "number",
                      "description": "Annual coupon rate as a percentage."
                    },
                    "currentYield": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Computed current yield (couponRate × faceValue / pricePerUnit)."
                    },
                    "yieldToMaturity": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Expected annualised return if held to maturity."
                    },
                    "modifiedDuration": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Modified duration in years."
                    },
                    "creditRating": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Credit rating (e.g. AAA, BBB+).",
                      "example": "BBB+"
                    },
                    "creditRatingAgency": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Agency that issued the credit rating (e.g. S&P, GCR Africa)."
                    },
                    "maturityDate": {
                      "type": "string",
                      "format": "date"
                    },
                    "faceValue": {
                      "type": "number"
                    },
                    "minInvestment": {
                      "type": "number"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "issuer": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "prospectusUrl": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Bond not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getBonds"
      }
    },
    "/funds": {
      "get": {
        "tags": [
          "Asset Classes"
        ],
        "summary": "List funds & ETFs",
        "description": "Returns all available money market funds, unit trusts, and ETFs. Includes NAV, annualised return, and minimum investment.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Filter by fund type.",
            "schema": {
              "type": "string",
              "enum": [
                "MMF",
                "UNIT_TRUST",
                "ETF"
              ]
            }
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "KE"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/funds?type=MMF\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/funds?type=MMF', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { funds } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/funds',\n  params={'type': 'MMF'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Fund list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "funds": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "description": "Current catalogue availability state. Only ACTIVE funds accept new subscriptions."
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "nav": {
                            "type": "number",
                            "description": "Current net asset value per unit."
                          },
                          "annualisedReturn": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "minInvestment": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listFunds"
      }
    },
    "/funds/{id}": {
      "get": {
        "tags": [
          "Asset Classes"
        ],
        "summary": "Fund detail",
        "description": "Returns full details for a specific fund or ETF.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "fund_mmf_africa"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/funds/fund_mmf_africa \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/funds/fund_mmf_africa', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/funds/fund_mmf_africa',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Fund detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "description": "Current catalogue availability state. Only ACTIVE funds accept new subscriptions."
                    },
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "type": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string"
                    },
                    "nav": {
                      "type": "number"
                    },
                    "annualisedReturn": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "minInvestment": {
                      "type": "number"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "managementFee": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "factsheetUrl": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getFunds"
      }
    },
    "/opportunities": {
      "get": {
        "tags": [
          "Asset Classes"
        ],
        "summary": "Private credit & pre-IPO",
        "description": "Returns open private credit deals and pre-IPO opportunities available to your sub-accounts. Each deal has a minimum ticket size, close date, and target return.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/opportunities \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/opportunities', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { opportunities } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/opportunities',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Open opportunities.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "opportunities": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "PRIVATE_CREDIT",
                              "PRE_IPO"
                            ]
                          },
                          "description": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "targetReturn": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Target annualised return %."
                          },
                          "minTicket": {
                            "type": "number",
                            "description": "Minimum investment in USD."
                          },
                          "closeDate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "closing_soon",
                              "closed"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listOpportunities"
      }
    },
    "/opportunities/{id}": {
      "get": {
        "tags": [
          "Asset Classes"
        ],
        "summary": "Opportunity or pre-IPO detail",
        "description": "Returns full detail for a single private market deal or pre-IPO offering. Resolves from the opportunities collection first, then preIPOOfferings. Accepts document ID or slug.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Opportunity document ID or URL slug.",
            "schema": {
              "type": "string",
              "example": "acme-series-b-2026"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/opportunities/acme-series-b-2026 \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Opportunity detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "assetType": {
                      "type": "string",
                      "enum": [
                        "OPPORTUNITY",
                        "PRE_IPO"
                      ]
                    },
                    "name": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "description": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "minInvestment": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "targetAmount": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "status": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Opportunity not found."
          }
        },
        "operationId": "getOpportunity"
      }
    },
    "/auto-register": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Auto-register sub-account",
        "description": "Single-call convenience endpoint that creates a sub-account **and** returns an API token for it.\nIdempotent on `uid` — calling twice with the same uid returns the existing sub-account and a fresh token.\nEquivalent to POST /users but returns auth context in one round trip.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutoRegisterRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/auto-register \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: auto_register_user_42\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"uid\":\"user_42\",\"email\":\"alice@yourapp.com\",\"name\":\"Alice K.\",\"country\":\"KE\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/auto-register', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'auto_register_user_42', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ uid: 'user_42', email: 'alice@yourapp.com', name: 'Alice K.', country: 'KE' }),\n});\nconst { subAccountId, token } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/auto-register',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'auto_register_user_42'},\n  json={'uid': 'user_42', 'email': 'alice@yourapp.com', 'name': 'Alice K.', 'country': 'KE'})\ndata = r.json()\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account created or retrieved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subAccountId": {
                      "type": "string"
                    },
                    "token": {
                      "type": "string",
                      "description": "Short-lived auth token for the sub-account."
                    },
                    "isNew": {
                      "type": "boolean",
                      "description": "true if this call created the account, false if it already existed."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createAutoRegister"
      }
    },
    "/users": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Create sub-account",
        "description": "Creates a new sub-account. Each sub-account has an isolated USD wallet, portfolio, and order book.\nThe `externalId` is your own reference — it must be unique per partner and is used in all subsequent calls.\nSub-accounts start with a `walletBalance` of $0 — fund them via POST `/users/{userId}/deposit`.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubAccountRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: create_user_8821\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"externalId\":\"usr_8821\",\"displayName\":\"Alice K.\",\"email\":\"alice@yourapp.com\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'create_user_8821', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ externalId: 'usr_8821', displayName: 'Alice K.', email: 'alice@yourapp.com' }),\n});\nconst { subAccountId } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/users',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'create_user_8821'},\n  json={'externalId': 'usr_8821', 'displayName': 'Alice K.', 'email': 'alice@yourapp.com'})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Sub-account created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          },
          "409": {
            "description": "externalId already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createUsers"
      },
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "List sub-accounts",
        "description": "Returns all sub-accounts for your partner key. Supports pagination and status filtering.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "frozen"
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from `nextCursor` of the previous response. Omit for the first page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/users?limit=20\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users?limit=20', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { accounts, count, hasMore, nextCursor } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users',\n  params={'limit': 20}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accounts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubAccount"
                      }
                    },
                    "users": {
                      "type": "array",
                      "deprecated": true,
                      "description": "Backward-compatible alias of accounts.",
                      "items": {
                        "$ref": "#/components/schemas/SubAccount"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listUsers"
      }
    },
    "/users/{userId}": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Get sub-account",
        "description": "Returns a single sub-account by its MyStocks `subAccountId`. Use this to check wallet balance, KYC status, and account state.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "description": "MyStocks sub-account ID (e.g. `usr_abc123`).",
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/users/usr_abc123 \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst user = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users/usr_abc123',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUsers"
      },
      "patch": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Update sub-account",
        "description": "Updates mutable fields on a sub-account. All fields are optional — only provided fields are changed.\nSet `status` to `frozen` to immediately suspend all trading, deposits, and withdrawals for a user.\nSet back to `active` to restore access.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSubAccountRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PATCH https://mystocks.africa/api/v1/partner/users/usr_abc123 \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: update_user_abc123_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"status\":\"frozen\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123', {\n  method: 'PATCH',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'update_user_abc123_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ status: 'frozen' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.patch('https://mystocks.africa/api/v1/partner/users/usr_abc123',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'update_user_abc123_001'},\n  json={'status': 'frozen'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated sub-account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          }
        },
        "operationId": "updateUsers"
      },
      "delete": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Close sub-account",
        "description": "Soft-closes/offboards a sub-account. This is a terminal state: closed sub-accounts cannot trade,\nreceive deposits, or withdraw. The record is retained for audit, regulatory, and support purposes.\n\nClosure is blocked while the sub-account has open orders, positive holdings, or unsettled proceeds.\nIf the USD wallet has residual cash, either withdraw it first or pass\n`residualCashHandling: \"transfer_to_partner\"` to atomically move the cash back to the partner master wallet\nduring closure.\n\nEmits `account.closed` to registered webhooks. Requires `Idempotency-Key`.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CloseSubAccountRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE https://mystocks.africa/api/v1/partner/users/usr_abc123 \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: close_usr_abc123_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"residualCashHandling\":\"transfer_to_partner\",\"reason\":\"User requested closure\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123', {\n  method: 'DELETE',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Idempotency-Key': crypto.randomUUID(),\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({ residualCashHandling: 'transfer_to_partner' }),\n});\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account closed, or was already closed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CloseSubAccountResponse"
                }
              }
            }
          },
          "409": {
            "description": "Closure blocked by open orders, positions, unsettled proceeds, or residual cash.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "closeUsers"
      }
    },
    "/users/{userId}/deposit": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Deposit to sub-account",
        "description": "Credits USD from your **master wallet** into a sub-account's wallet.\nYour master wallet must have sufficient balance — check via GET /account.\n\n**Managed FX model** — pass a USD ledger amount (`amount` or `amountUsd`) or pass `amount` with a non-USD\n`currency` such as `KES`. MyStocks converts supported non-USD amounts into the USD ledger using the managed\nFX table from GET `/fx/rates`.\n\nPass `Idempotency-Key` to make deposit retries safe on network failure.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DepositRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/deposit \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: dep_usr_abc123_1743152580\" \\\n  -d '{\"amount\":65000,\"currency\":\"KES\",\"note\":\"Mpesa STK push ref KE2482\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/deposit', {\n  method: 'POST',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Content-Type': 'application/json',\n    'Idempotency-Key': 'dep_usr_abc123_1743152580',\n  },\n  body: JSON.stringify({ amount: 65000, currency: 'KES', note: 'Mpesa STK push' }),\n});\nconst { walletBalance } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/deposit',\n  headers={\n    'Authorization': 'Bearer pk_live_KEY',\n    'Idempotency-Key': 'dep_usr_abc123_1743152580',\n  },\n  json={'amount': 65000, 'currency': 'KES', 'note': 'Mpesa STK push'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Deposit processed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "walletBalance": {
                      "type": "number",
                      "description": "New wallet balance after deposit."
                    },
                    "transactionId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Master wallet has insufficient balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency conflict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createDeposit"
      }
    },
    "/users/{userId}/withdraw": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Withdraw from sub-account",
        "description": "Moves USD from a sub-account's wallet back to your **master wallet**.\nUse this when your user requests a cash-out. The funds are immediately available in your master wallet.\nPass a USD ledger amount or a supported local `amount` + `currency`; non-USD values are converted with\nMyStocks managed FX.\nPass `Idempotency-Key` to make withdrawals safe to retry.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/withdraw \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: wd_usr_abc123_1743152999\" \\\n  -d '{\"amount\":26000,\"currency\":\"KES\",\"note\":\"User requested withdrawal\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/withdraw', {\n  method: 'POST',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Content-Type': 'application/json',\n    'Idempotency-Key': `wd_${userId}_${Date.now()}`,\n  },\n  body: JSON.stringify({ amount: 26000, currency: 'KES', note: 'User withdrawal' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests, time\nrequests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/withdraw',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': f'wd_abc123_{int(time.time())}'},\n  json={'amount': 26000, 'currency': 'KES'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Withdrawal processed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "walletBalance": {
                      "type": "number",
                      "description": "New sub-account wallet balance."
                    },
                    "transactionId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Sub-account has insufficient wallet balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createWithdraw"
      }
    },
    "/users/{userId}/wallet": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account wallet",
        "description": "Returns the current wallet balance and recent transaction history for a sub-account.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/users/usr_abc123/wallet \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/wallet', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { balance, transactions } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users/usr_abc123/wallet',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet and recent transactions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "balance": {
                      "type": "number",
                      "description": "Current USD wallet balance."
                    },
                    "subAccountId": {
                      "type": "string"
                    },
                    "externalId": {
                      "type": "string"
                    },
                    "wallet": {
                      "type": "object",
                      "properties": {
                        "currency": {
                          "type": "string",
                          "example": "USD"
                        },
                        "balance": {
                          "type": "number"
                        }
                      }
                    },
                    "wallets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WalletView"
                      }
                    },
                    "managedFx": {
                      "$ref": "#/components/schemas/ManagedFx"
                    },
                    "transactions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "DEPOSIT",
                              "WITHDRAWAL",
                              "TRADE_ESCROW",
                              "TRADE_CREDIT",
                              "DIVIDEND"
                            ]
                          },
                          "amount": {
                            "type": "number"
                          },
                          "note": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listWallet"
      }
    },
    "/users/{userId}/buying-power": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account buying power (settlement-aware)",
        "description": "Settlement-aware balance for a sub-account. `buyingPowerUsd` is cash available to trade (unsettled SELL proceeds are usable for new buys, as is standard). `withdrawableUsd` excludes unsettled SELL proceeds, which remain locked until exchange settlement (T+N) — this is the free-riding guard. `settlements[]` lists each unsettled tranche and its settlement date.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Settlement-aware balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuyingPower"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSubAccountBuyingPower"
      }
    },
    "/users/{userId}/preflight": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Preflight a sub-account trade",
        "description": "Side-effect-free eligibility check combining account and KYC status, market session, indicative pricing, buying power or holdings, estimated fees, and settlement expectations. Obtain a firm quoteId after this check passes and before submitting the order.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "BUY",
                "SELL"
              ]
            }
          },
          {
            "name": "quantity",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "exclusiveMinimum": 0,
              "example": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Consolidated trade eligibility result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "eligible",
                    "blockingReasons",
                    "account",
                    "market",
                    "quote",
                    "buyingPower",
                    "settlement",
                    "nextStep"
                  ],
                  "properties": {
                    "eligible": {
                      "type": "boolean"
                    },
                    "blockingReasons": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "code",
                          "message"
                        ],
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "account": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "market": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "quote": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "buyingPower": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "settlement": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "nextStep": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid symbol, side, or quantity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account or symbol not found."
          }
        },
        "operationId": "getTradePreflight"
      }
    },
    "/stream": {
      "get": {
        "x-sandbox": true,
        "tags": [
          "Observability"
        ],
        "summary": "Real-time event stream (Server-Sent Events)",
        "description": "A Server-Sent Events (text/event-stream) feed of the partner's real-time events — order.filled/rejected/cancelled/triggered/pending, deposit/ withdraw/wallet, kyc.updated, topup.confirmed, float.low, dividend.paid, corporateaction.declared, quote.expired, market.status, and more. Every event that fires a webhook is also streamed here, so a partner can react in real time without registering a webhook. Auth: Authorization Bearer pk_live_ (server) or ?access_token= for browser EventSource (prefer a short-lived ms_oauth_ token). Each frame carries an id (ms cursor); reconnect with Last-Event-ID or ?since= to resume. Connections cap at ~110s then close with an event stream_timeout frame — reconnect to continue. Heartbeat comments (: ping) every 15s.\n",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Resume cursor (event id / ms epoch). Replays at most the last hour."
          }
        ],
        "responses": {
          "200": {
            "description": "An event stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "operationId": "streamEvents"
      }
    },
    "/price-alerts": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Market Data"
        ],
        "summary": "List price alerts",
        "description": "Returns the price alerts registered by this partner.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ARMED",
                "TRIGGERED",
                "DISARMED"
              ]
            },
            "description": "Filter by alert state."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The partner's price alerts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PriceAlert"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPriceAlerts"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Market Data"
        ],
        "summary": "Create a price alert",
        "description": "Registers a price alert. When the latest delayed price observation crosses the threshold while the exchange is open,\na `price.alert` webhook fires and the event is streamed on GET /stream.\n\n`threshold` is in the instrument's LOCAL trading currency — the same basis as a quote's `price`.\n\nAlerts are evaluated on the same ~15-minute polling cycle as quotes, so an alert fires on the\nfirst poll after the crossing, not at the instant of the tick. Do not use a price alert as an\nexecution trigger — place a resting LIMIT or STOP order instead.\n\nMaximum 200 active alerts per partner. Requires a full key (pk_live_).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "symbol",
                  "exchange",
                  "condition",
                  "threshold"
                ],
                "properties": {
                  "symbol": {
                    "type": "string",
                    "example": "SCOM.KE"
                  },
                  "exchange": {
                    "type": "string",
                    "example": "NSE"
                  },
                  "condition": {
                    "type": "string",
                    "enum": [
                      "above",
                      "below"
                    ]
                  },
                  "threshold": {
                    "type": "number",
                    "description": "Positive, in the instrument's local trading currency.",
                    "example": 20
                  },
                  "repeat": {
                    "type": "boolean",
                    "default": false,
                    "description": "When false (default) the alert fires once and disarms. When true it re-arms, but only\nafter the price crosses back through the threshold, so a price hovering at the\nthreshold cannot spam your endpoint.\n"
                  },
                  "clientAlertId": {
                    "type": "string",
                    "description": "Your own reference, echoed back on the alert and in the webhook payload."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/price-alerts \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: 8f14e45f-ceea-467a-9575-9f0c0b6f1f2a\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"symbol\": \"SCOM.KE\", \"exchange\": \"NSE\", \"condition\": \"above\", \"threshold\": 20.00}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/price-alerts', {\n  method: 'POST',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Idempotency-Key': crypto.randomUUID(),\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({ symbol: 'SCOM.KE', exchange: 'NSE', condition: 'above', threshold: 20 }),\n});\nconst alert = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests, uuid\nr = requests.post('https://mystocks.africa/api/v1/partner/price-alerts',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': str(uuid.uuid4())},\n  json={'symbol': 'SCOM.KE', 'exchange': 'NSE', 'condition': 'above', 'threshold': 20.00})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Alert created and armed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceAlert"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, unknown symbol, or the active-alert cap was reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createPriceAlert"
      }
    },
    "/price-alerts/{alertId}": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Market Data"
        ],
        "summary": "Get a price alert",
        "parameters": [
          {
            "name": "alertId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "alr_abc123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The price alert.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PriceAlert"
                }
              }
            }
          },
          "404": {
            "description": "Price alert not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getPriceAlert"
      },
      "delete": {
        "x-sandbox": false,
        "tags": [
          "Market Data"
        ],
        "summary": "Delete a price alert",
        "description": "Removes a price alert. Requires a full key (pk_live_).",
        "parameters": [
          {
            "name": "alertId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "alr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Alert deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "alertId": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Price alert not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deletePriceAlert"
      }
    },
    "/pricing": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Get partner markup pricing",
        "description": "The partner's self-serve markup config — a default markup (bps) added on top of the MyStocks base fee, plus per-symbol / per-exchange / per-asset-class overrides and an optional promo window. Resolution for a trade is most-specific-wins: active promo → symbol → exchange → assetClass → default. Markup is capped at maxMarkupBps.\n",
        "responses": {
          "200": {
            "description": "Current pricing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerPricing"
                }
              }
            }
          }
        },
        "operationId": "getPricing"
      },
      "patch": {
        "tags": [
          "Settings"
        ],
        "summary": "Update partner markup pricing",
        "description": "Full replacement of the markup config. Requires a full API key. Each markupBps is 0–maxMarkupBps.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "defaultMarkupBps"
                ],
                "properties": {
                  "defaultMarkupBps": {
                    "type": "number",
                    "minimum": 0,
                    "example": 75
                  },
                  "overrides": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "scope",
                        "key",
                        "markupBps"
                      ],
                      "properties": {
                        "scope": {
                          "type": "string",
                          "enum": [
                            "symbol",
                            "exchange",
                            "assetClass"
                          ]
                        },
                        "key": {
                          "type": "string",
                          "example": "SCOM.KE"
                        },
                        "markupBps": {
                          "type": "number",
                          "minimum": 0,
                          "example": 50
                        }
                      }
                    }
                  },
                  "promo": {
                    "type": "object",
                    "properties": {
                      "markupBps": {
                        "type": "number",
                        "minimum": 0,
                        "example": 0
                      },
                      "until": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              },
              "examples": {
                "perExchange": {
                  "value": {
                    "defaultMarkupBps": 75,
                    "overrides": [
                      {
                        "scope": "exchange",
                        "key": "NSE",
                        "markupBps": 50
                      },
                      {
                        "scope": "symbol",
                        "key": "SCOM.KE",
                        "markupBps": 25
                      }
                    ],
                    "promo": {
                      "markupBps": 0,
                      "until": "2026-08-01T00:00:00Z"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated pricing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerPricing"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updatePricing"
      }
    },
    "/float": {
      "get": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "Master-account float / funding headroom",
        "description": "The partner aggregates their end-users' deposits and remits to the master account (POST /topup); the master wallet funds sub-account deposits. This returns the funding headroom so the partner knows when to remit: masterBalanceUsd, pending top-ups, any credit line, availableToFundUsd, and a floatStatus of HEALTHY / LOW / DEPLETED / ON_CREDIT. Pair with the float.low and topup.confirmed webhooks.\n",
        "responses": {
          "200": {
            "description": "Float status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FloatStatus"
                }
              }
            }
          }
        },
        "operationId": "getFloat"
      },
      "patch": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "Update treasury float controls",
        "description": "Sets lowBalanceThresholdUsd — the master balance below which a float.low webhook fires. creditLimitUsd is set by MyStocks only.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lowBalanceThresholdUsd": {
                    "type": "number",
                    "minimum": 0,
                    "example": 500
                  },
                  "minimumFloatUsd": {
                    "type": "number",
                    "minimum": 0,
                    "example": 5000
                  },
                  "autoTopupReminderEnabled": {
                    "type": "boolean",
                    "example": true
                  },
                  "autoTopupReminderRecipients": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "autoTopupReminderFrequencyHours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 168,
                    "example": 24
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated float status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FloatStatus"
                }
              }
            }
          }
        },
        "operationId": "updateFloatThreshold"
      },
      "post": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "Request a credit-limit change",
        "description": "Opens a MyStocks treasury review workflow for a partner credit-limit increase or decrease. Requires a full key and Idempotency-Key.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "requestedCreditLimitUsd"
                ],
                "properties": {
                  "requestedCreditLimitUsd": {
                    "type": "number",
                    "minimum": 0,
                    "example": 25000
                  },
                  "expectedMonthlyVolumeUsd": {
                    "type": "number",
                    "minimum": 0,
                    "example": 500000
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 2000,
                    "example": "Higher expected deposit volume after launch."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Credit-limit request opened.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requestId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "example": "PENDING"
                    },
                    "currentCreditLimitUsd": {
                      "type": "number"
                    },
                    "requestedCreditLimitUsd": {
                      "type": "number"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "A credit-limit request is already pending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createFloatCreditLimitRequest"
      }
    },
    "/users/{userId}/tax-lots": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Open tax lots (sub-account)",
        "description": "Open tax lots for a sub-account — each remaining acquired quantity with per-unit cost basis and acquisition date, derived FIFO from settled orders. Pair with /gains for realized disposals.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter to one symbol."
          }
        ],
        "responses": {
          "200": {
            "description": "Open lots.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxLots"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSubAccountTaxLots"
      }
    },
    "/users/{userId}/gains": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Realized capital gains (sub-account)",
        "description": "Realized gains/losses for a sub-account — each disposal FIFO-matched to its acquiring lot, with acquisition + disposal dates, cost basis, proceeds, holding period, and gain. Feed a capital-gains tax statement.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Realized disposals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RealizedGains"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSubAccountGains"
      }
    },
    "/tax-lots": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Open tax lots (master account)",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Open lots.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxLots"
                }
              }
            }
          }
        },
        "operationId": "getTaxLots"
      }
    },
    "/gains": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Realized capital gains (master account)",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Realized disposals.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RealizedGains"
                }
              }
            }
          }
        },
        "operationId": "getGains"
      }
    },
    "/buying-power": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Master account buying power (settlement-aware)",
        "description": "Settlement-aware balance for the partner master account. See /users/{userId}/buying-power for field semantics.\n",
        "responses": {
          "200": {
            "description": "Settlement-aware balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuyingPower"
                }
              }
            }
          }
        },
        "operationId": "getBuyingPower"
      }
    },
    "/users/{userId}/trade": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Trade on behalf",
        "description": "Places a BUY or SELL order on behalf of a sub-account.\nThe sub-account's wallet is escrowed for BUY orders; holdings are checked for SELL.\nThe sub-account must have `kycStatus: VERIFIED` to trade.\nPass `Idempotency-Key` to make trade retries safe.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TradeRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/trade \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: tr_usr_abc123_1743153100\" \\\n  -d '{\"symbol\":\"SCOM.KE\",\"type\":\"BUY\",\"quantity\":500}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/trade', {\n  method: 'POST',\n  headers: {\n    Authorization: 'Bearer pk_live_KEY',\n    'Content-Type': 'application/json',\n    'Idempotency-Key': `tr_usr_abc123_${Date.now()}`,\n  },\n  body: JSON.stringify({ symbol: 'SCOM.KE', type: 'BUY', quantity: 500 }),\n});\nconst { orderId } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests, time\nr = requests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/trade',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': f'tr_abc123_{int(time.time())}'},\n  json={'symbol': 'SCOM.KE', 'type': 'BUY', 'quantity': 500})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Order placed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "400": {
            "description": "Sub-account has insufficient wallet balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Sub-account is frozen or KYC not verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Market closed, ambiguous symbol, or invalid quantity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createUserTrade"
      }
    },
    "/users/{userId}/portfolio": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account portfolio",
        "description": "Returns all open equity positions held by a sub-account, with current market value and unrealised P&L in USD.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/users/usr_abc123/portfolio \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/portfolio', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { holdings, summary } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users/usr_abc123/portfolio',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account portfolio holdings and aggregate values.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "subAccountId": {
                      "type": "string"
                    },
                    "externalId": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "walletBalance": {
                          "type": "number"
                        },
                        "portfolioValue": {
                          "type": "number"
                        },
                        "investedCapital": {
                          "type": "number"
                        },
                        "unrealisedPnl": {
                          "type": "number"
                        },
                        "totalValue": {
                          "type": "number"
                        }
                      }
                    },
                    "holdings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "stockName": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "exchange": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "currency": {
                            "type": "string"
                          },
                          "units": {
                            "type": "number"
                          },
                          "avgCost": {
                            "type": "number"
                          },
                          "currentUsdPrice": {
                            "type": "number"
                          },
                          "localPrice": {
                            "type": "number"
                          },
                          "currentValue": {
                            "type": "number"
                          },
                          "investedCapital": {
                            "type": "number"
                          },
                          "unrealisedPnl": {
                            "type": "number"
                          },
                          "priceIsLive": {
                            "type": "boolean",
                            "description": "Backwards-compatible freshness flag for the delayed production catalogue; never implies real-time tick data."
                          },
                          "lastPriceUpdate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "asOf": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time",
                            "description": "Upstream capture timestamp for the price used in this valuation."
                          },
                          "stale": {
                            "type": "boolean"
                          },
                          "priceSource": {
                            "type": "string",
                            "enum": [
                              "PRODUCTION_CATALOGUE",
                              "SANDBOX_HOLDING"
                            ]
                          },
                          "dataQuality": {
                            "$ref": "#/components/schemas/QuoteDataQuality"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getUserPortfolio"
      }
    },
    "/users/{userId}/portfolio/history": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account equity history",
        "description": "Daily raw-equity history for one sub-account owned by the authenticated partner. Values include wallet cash and holdings but are not cash-flow adjusted, so they must not be presented as investment P&L.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1M",
                "3M",
                "6M",
                "1Y",
                "ALL"
              ],
              "default": "1Y"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account daily equity points.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortfolioHistoryResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid period or date range."
          },
          "404": {
            "description": "Sub-account not found or not owned by this partner."
          }
        },
        "operationId": "getUserPortfolioHistory"
      }
    },
    "/users/{userId}/portfolio/performance": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account cash-flow-adjusted performance",
        "description": "Returns daily P&L, Modified Dietz interval returns, and geometrically linked cumulative time-weighted return for one partner-owned sub-account. Completed deposits and withdrawals are treated as external cash flows. Supply an optional exchange-qualified benchmark symbol for rebased price return and excess-return comparison.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1M",
                "3M",
                "6M",
                "1Y",
                "ALL"
              ],
              "default": "1Y"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "benchmark",
            "in": "query",
            "description": "Optional exchange-qualified benchmark symbol.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cash-flow-adjusted sub-account performance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortfolioPerformanceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid period",
            "date range": null,
            "or benchmark format.": null
          },
          "404": {
            "description": "Sub-account or benchmark symbol not found."
          }
        },
        "operationId": "getUserPortfolioPerformance"
      }
    },
    "/users/{userId}/orders": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account orders",
        "description": "Returns order history for a sub-account. Symbol and status filtering are performed without a Firestore composite-index dependency and work identically in production and sandbox.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "WORKING",
                "PARTIALLY_FILLED",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED",
                "EXPIRED"
              ],
              "description": "FILLED is canonical. COMPLETED and PROCESSING are legacy compatibility aliases."
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "description": "Exact, case-insensitive exchange-qualified symbol (for example `SCOM.KE`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from `nextCursor` of the previous response. Omit for the first page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/users/usr_abc123/orders?status=FILLED&symbol=SCOM.KE\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders?status=FILLED&symbol=SCOM.KE',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { orders } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users/usr_abc123/orders',\n  params={'status': 'FILLED', 'symbol': 'SCOM.KE'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listUserOrders"
      }
    },
    "/users/{userId}/orders/{orderId}": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Get sub-account order",
        "description": "Returns a single order for a sub-account by order ID.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "404": {
            "description": "Order not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUserOrder"
      },
      "delete": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Cancel sub-account order",
        "description": "Cancels an active PENDING or WORKING order on a sub-account and releases reserved cash or units. Available in production and sandbox; terminal orders return a conflict/error response.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE \\\n  https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: cancel_user_order_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz',\n  { method: 'DELETE', headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'cancel_user_order_001' } }\n);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.delete(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'cancel_user_order_001'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order cancelled. BUY escrow refunded to sub-account wallet atomically.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orderId": {
                      "type": "string"
                    },
                    "subAccountId": {
                      "type": "string"
                    },
                    "externalId": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "BUY",
                        "SELL"
                      ]
                    },
                    "symbol": {
                      "type": "string"
                    },
                    "quantity": {
                      "type": "number"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "CANCELLED"
                      ]
                    },
                    "refunded": {
                      "type": "number",
                      "description": "USD amount returned to sub-account wallet. Present for BUY orders only."
                    },
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Order cannot be cancelled — it is no longer PENDING.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteUsersUserIdOrdersOrderId"
      },
      "patch": {
        "x-sandbox": false,
        "x-sandbox-stub": true,
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Replace (modify) a resting order",
        "description": "Modifies a **WORKING** resting order (LIMIT / STOP / STOP_LIMIT) in place, keeping the same\n`orderId`. Supply any of `limitPrice`, `stopPrice`, `quantity` — the BUY escrow or SELL unit\nreservation is adjusted by the delta atomically. Prices are in the stock's LOCAL trading currency.\n\nOnly WORKING resting orders can be modified — market orders (PENDING for automatic internal-book execution) and\nterminal orders return `409`. Increasing a BUY escrow beyond the available wallet balance returns\n`400 INSUFFICIENT_FUNDS`; increasing a SELL quantity beyond available units returns `409`.\nFires the `order.replaced` webhook and writes a REPLACE execution report.\n\nA unique `Idempotency-Key` header is **required** (this moves escrow).\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "At least one of limitPrice, stopPrice, quantity must be supplied and differ from the current order.",
                "properties": {
                  "limitPrice": {
                    "type": "number",
                    "description": "New limit price in the stock's local currency (LIMIT / STOP_LIMIT).",
                    "example": 21.5
                  },
                  "stopPrice": {
                    "type": "number",
                    "description": "New stop price in the stock's local currency (STOP / STOP_LIMIT).",
                    "example": 20
                  },
                  "quantity": {
                    "type": "number",
                    "description": "New order quantity. Converts a cashValue-sized order to quantity-sized.",
                    "example": 150
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PATCH \\\n  https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: replace_ord_abc123xyz_1\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"limitPrice\": 21.5, \"quantity\": 150}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz',\n  { method: 'PATCH',\n    headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'replace_ord_abc123xyz_1', 'Content-Type': 'application/json' },\n    body: JSON.stringify({ limitPrice: 21.5, quantity: 150 }) }\n);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.patch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'replace_ord_abc123xyz_1'},\n  json={'limitPrice': 21.5, 'quantity': 150})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Order modified. Escrow / reservation adjusted by the delta.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Order"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "executionReportId": {
                          "type": "string"
                        },
                        "escrowAdjustment": {
                          "type": "number",
                          "description": "USD escrow delta for BUY orders (>0 debited, <0 refunded)."
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or insufficient funds to increase a BUY escrow.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Order is not a WORKING resting order, or a concurrent change won the race.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "replaceUserOrder"
      }
    },
    "/users/{userId}/orders/{orderId}/executions": {
      "get": {
        "x-sandbox": true,
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Get sub-account order execution reports",
        "description": "Returns OMS execution reports for a sub-account order, including receipt, fill, rejection, and cancellation reports.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ord_abc123xyz"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Execution reports for the order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecutionReportsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account or order not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUserOrderExecutions"
      }
    },
    "/users/{userId}/transactions": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account transactions",
        "description": "Returns the full ledger transaction history for a sub-account — deposits, withdrawals, trade escrows (INVEST), trade settlements (SELL), dividend distributions, fund redemptions, and fee entries. Cursor-paginated; use `nextCursor` from the previous response as the `cursor` query param to fetch the next page.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Filter to a single transaction type.",
            "schema": {
              "type": "string",
              "enum": [
                "DEPOSIT",
                "WITHDRAWAL",
                "INVEST",
                "SELL",
                "DISTRIBUTION",
                "REDEEM",
                "FEE",
                "TRANSFER_IN",
                "TRANSFER_OUT"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Return transactions on or after this date (YYYY-MM-DD, UTC).",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-01-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Return transactions on or before this date (YYYY-MM-DD, UTC).",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-06-30"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from `nextCursor` of the previous response. Omit for the first page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/users/usr_abc123/transactions?limit=20&type=DEPOSIT\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/transactions?limit=20&type=DEPOSIT',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { transactions, hasMore, nextCursor } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/transactions',\n  params={'limit': 20, 'type': 'DEPOSIT'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Transaction ledger.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transactions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "DEPOSIT",
                              "WITHDRAWAL",
                              "INVEST",
                              "SELL",
                              "DISTRIBUTION",
                              "REDEEM",
                              "FEE",
                              "TRANSFER_IN",
                              "TRANSFER_OUT"
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "PENDING",
                              "COMPLETED",
                              "FAILED"
                            ]
                          },
                          "direction": {
                            "type": "string",
                            "enum": [
                              "CREDIT",
                              "DEBIT"
                            ]
                          },
                          "amount": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string",
                            "example": "USD"
                          },
                          "description": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "reference": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "settledAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time",
                            "description": "Settlement timestamp for completed rows. Legacy completed records return their immutable completion/creation timestamp when the original field was absent."
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listTransactions"
      }
    },
    "/users/{userId}/kyc": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "KYC assertion",
        "description": "Asserts the KYC status of a sub-account. MyStocks defers identity verification to your platform —\nyou send us the result of your own KYC process.\n\n`VERIFIED` + `BASIC` is sufficient to unlock trading and asset subscriptions.\n`FULL` is required for higher transaction limits and private credit deals.\n`REJECTED` keeps the account gated and requires `reasonCode` plus customer-safe\nremediation text in `reason`; the response and `kyc.updated` webhook include `reviewedAt`.\n\nThe `reference` field stores your internal KYC session ID for audit correlation.\n\nYou may also attach an optional structured compliance profile (ID document type,\ndate of birth, nationality, tax residency, PEP/sanctions screening result, risk\nrating, consent timestamp, evidence reference). Sensitive identifiers — `idNumber`\nand `taxId` — are stored as a sha256 fingerprint plus last-4 only and are never\nreturned in full. All profile fields are optional and additive.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KycRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/kyc \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: kyc_usr_abc123_1743152580\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"status\":\"VERIFIED\",\"level\":\"BASIC\",\"reference\":\"kyc_session_88721\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/kyc', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'kyc_usr_abc123_1743152580', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ status: 'VERIFIED', level: 'BASIC', reference: 'kyc_session_88721' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/kyc',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'kyc_usr_abc123_1743152580'},\n  json={'status': 'VERIFIED', 'level': 'BASIC', 'reference': 'kyc_session_88721'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "KYC status updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "kycStatus": {
                      "type": "string",
                      "enum": [
                        "NONE",
                        "PENDING",
                        "VERIFIED",
                        "REJECTED"
                      ]
                    },
                    "kycLevel": {
                      "type": "string",
                      "enum": [
                        "NONE",
                        "BASIC",
                        "FULL"
                      ]
                    },
                    "reasonCode": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "reviewedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "profile": {
                      "type": "object",
                      "description": "Echo of the compliance fields asserted on this call (present only when a profile was supplied). Sensitive identifiers appear as idNumberLast4 / taxIdLast4 — never in full."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createKyc"
      }
    },
    "/users/{userId}/devices": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "List notification devices",
        "description": "Lists mobile/web push devices registered for a sub-account. Tokens are never returned; only tokenLast4 is exposed for support correlation.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Registered notification devices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "devices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/NotificationDevice"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listUserDevices"
      },
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Register notification device",
        "description": "Registers or updates a push-token record for your mobile/web app. MyStocks stores only a token hash and last4.\n\nThis endpoint gives partners a durable device-token model for mobile notification fanout. Webhooks remain the\nserver-side event source of truth; your backend should receive webhooks or SSE events and fan out user-visible\nnotifications through APNs, FCM, Expo, or Web Push using the device records you maintain.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeviceRegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing device registration updated."
          },
          "201": {
            "description": "Device registered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "subAccountId": {
                      "type": "string"
                    },
                    "externalId": {
                      "type": "string"
                    },
                    "device": {
                      "$ref": "#/components/schemas/NotificationDevice"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "registerUserDevice"
      }
    },
    "/users/{userId}/devices/{deviceId}": {
      "delete": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Revoke notification device",
        "description": "Soft-revokes a registered push device. The record is retained for audit and support correlation.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "deviceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "dev_6f9a4d2c1b0e8a7c3d5f9012"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Device revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "subAccountId": {
                      "type": "string"
                    },
                    "externalId": {
                      "type": "string"
                    },
                    "deviceId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "example": "revoked"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Device not found."
          }
        },
        "operationId": "revokeUserDevice"
      }
    },
    "/users/{userId}/subscribe": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Subscribe (bond/fund/deal)",
        "description": "Subscribes a sub-account to a bond, money market fund, or private credit/pre-IPO deal.\nFunds are escrowed from the sub-account wallet on submission.\n\nFor **FUND** subscriptions: specify `units`. For **OPPORTUNITY** / **PRE_IPO**: specify `amount` in USD.\nThe sub-account must be KYC-verified (`kycStatus: VERIFIED`).\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/subscribe \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: sub_usr_abc123_1743152580\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"assetType\":\"FUND\",\"assetId\":\"fund_mmf_africa\",\"units\":500}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/subscribe', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'sub_usr_abc123_1743152580', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ assetType: 'FUND', assetId: 'fund_mmf_africa', units: 500 }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/subscribe',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'sub_usr_abc123_1743152580'},\n  json={'assetType': 'FUND', 'assetId': 'fund_mmf_africa', 'units': 500})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Subscription created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "subscriptionId": {
                      "type": "string"
                    },
                    "amountEscrowed": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Insufficient wallet balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "KYC not verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createSubscribe"
      }
    },
    "/users/{userId}/redeem": {
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Redeem fund units",
        "description": "Redeems fund units back to the sub-account wallet. Proceeds are credited at the current NAV once the redemption is processed by the fund manager (typically T+1 for MMFs).",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeemRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/redeem \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: red_usr_abc123_1743152580\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"holdingId\":\"fund_mmf_africa\",\"unitsToRedeem\":200}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/users/usr_abc123/redeem', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'red_usr_abc123_1743152580', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ holdingId: 'fund_mmf_africa', unitsToRedeem: 200 }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/redeem',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'red_usr_abc123_1743152580'},\n  json={'holdingId': 'fund_mmf_africa', 'unitsToRedeem': 200})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Redemption queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "redemptionId": {
                      "type": "string"
                    },
                    "unitsRedeemed": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createRedeem"
      }
    },
    "/users/{userId}/dividends": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account dividends",
        "description": "Returns dividend payments received by a sub-account, including declared date, pay date, and USD amount credited.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/users/usr_abc123/dividends \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/dividends',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { dividends } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users/usr_abc123/dividends',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Dividend history.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dividends": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "amountUsd": {
                            "type": "number"
                          },
                          "sharesHeld": {
                            "type": "integer"
                          },
                          "dividendPerShare": {
                            "type": "number"
                          },
                          "exDate": {
                            "type": "string",
                            "format": "date"
                          },
                          "payDate": {
                            "type": "string",
                            "format": "date"
                          },
                          "creditedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listDividends"
      }
    },
    "/users/{userId}/watchlist": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "List sub-account watchlist",
        "description": "Returns the watchlist for a sub-account, including delayed price and day-change for each stock where available.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Watchlist items with delayed prices and freshness metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "assetId": {
                            "type": "string"
                          },
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": "string"
                          },
                          "sourceType": {
                            "type": "string",
                            "example": "LISTED_STOCK"
                          },
                          "addedAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "price": {
                            "type": "number",
                            "description": "Live local-currency price (omitted if unavailable)."
                          },
                          "usdPrice": {
                            "type": "number"
                          },
                          "change": {
                            "type": "number",
                            "description": "Day change percent."
                          },
                          "currency": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listWatchlist"
      },
      "post": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Add symbol to watchlist",
        "description": "Adds a stock to a sub-account's watchlist. The symbol is resolved to its canonical\nexchange-qualified form (e.g. \"SCOM\" → \"SCOM.KE\"). Returns 409 if already present.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "symbol"
                ],
                "properties": {
                  "symbol": {
                    "type": "string",
                    "description": "Ticker symbol (exchange-qualified preferred, e.g. SCOM.KE).",
                    "example": "SCOM.KE"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist \\\n  -H \"x-api-key: pk_live_KEY\" \\\n  -H \"Idempotency-Key: watchlist_add_scom_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"symbol\":\"SCOM.KE\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist',\n  {\n    method: 'POST',\n    headers: { 'x-api-key': 'pk_live_KEY', 'Idempotency-Key': 'watchlist_add_scom_001', 'Content-Type': 'application/json' },\n    body: JSON.stringify({ symbol: 'SCOM.KE' }),\n  }\n);\nconst item = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist',\n  headers={'x-api-key': 'pk_live_KEY', 'Idempotency-Key': 'watchlist_add_scom_001'},\n  json={'symbol': 'SCOM.KE'})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Symbol added to watchlist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "assetId": {
                      "type": "string"
                    },
                    "symbol": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "exchange": {
                      "type": "string"
                    },
                    "addedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Symbol already on watchlist."
          }
        },
        "operationId": "addToWatchlist"
      }
    },
    "/users/{userId}/watchlist/{symbol}": {
      "delete": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Remove symbol from watchlist",
        "description": "Removes a stock from a sub-account's watchlist. Returns 404 if the symbol is not on the watchlist.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            },
            "description": "Exchange-qualified ticker symbol (e.g. SCOM.KE)."
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist/SCOM.KE \\\n  -H \"x-api-key: pk_live_KEY\" \\\n  -H \"Idempotency-Key: watchlist_remove_scom_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch(\n  'https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist/SCOM.KE',\n  { method: 'DELETE', headers: { 'x-api-key': 'pk_live_KEY', 'Idempotency-Key': 'watchlist_remove_scom_001' } }\n);\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.delete('https://mystocks.africa/api/v1/partner/users/usr_abc123/watchlist/SCOM.KE',\n  headers={'x-api-key': 'pk_live_KEY', 'Idempotency-Key': 'watchlist_remove_scom_001'})\n"
          }
        ],
        "responses": {
          "204": {
            "description": "Symbol removed. No response body."
          },
          "404": {
            "description": "Symbol not on watchlist or sub-account not found."
          }
        },
        "operationId": "removeFromWatchlist"
      }
    },
    "/client-activity": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Client activity feed",
        "description": "Returns a chronological feed of all sub-account activity — trades, deposits, withdrawals, KYC changes, and dividends. Useful for building an admin dashboard.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-01-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Filter by event type.",
            "schema": {
              "type": "string",
              "enum": [
                "TRADE",
                "DEPOSIT",
                "WITHDRAWAL",
                "KYC",
                "DIVIDEND"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 500
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/client-activity?from=2026-01-01&limit=50\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/client-activity?from=2026-01-01',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { events } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/client-activity',\n  params={'from': '2026-01-01', 'limit': 50},\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Activity events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "eventId": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "subAccountId": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "object",
                            "description": "Event-specific payload."
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listClientActivity"
      }
    },
    "/report/aum": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Report — AUM",
        "description": "Returns assets under management across all sub-accounts, broken down by exchange, asset class, and currency.",
        "parameters": [
          {
            "name": "asOf",
            "in": "query",
            "description": "Report date. Defaults to today.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/report/aum?asOf=2026-05-01\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/report/aum', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { totalAumUsd, breakdown } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/report/aum',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "AUM report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asOf": {
                      "type": "string",
                      "format": "date"
                    },
                    "totalAumUsd": {
                      "type": "number"
                    },
                    "totalAccounts": {
                      "type": "integer"
                    },
                    "breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "assetClass": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "valueUsd": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listAum"
      }
    },
    "/report/positions": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Report — Positions",
        "description": "Returns all open positions across all sub-accounts, aggregated by symbol. Useful for calculating exposure and hedging needs.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/report/positions \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/report/positions', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { positions } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/report/positions',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregated positions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "positions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": "string"
                          },
                          "totalQuantity": {
                            "type": "integer"
                          },
                          "totalValueUsd": {
                            "type": "number"
                          },
                          "accountCount": {
                            "type": "integer",
                            "description": "Number of sub-accounts holding this position."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPositions"
      }
    },
    "/report/fees": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Report — Fees",
        "description": "Returns fees earned by MyStocks and your markup fees, broken down by period and sub-account.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/report/fees?from=2026-01-01&to=2026-03-31\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/report/fees?from=2026-01-01&to=2026-03-31',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { totalFeesUsd, breakdown } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/report/fees',\n  params={'from': '2026-01-01', 'to': '2026-03-31'},\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Fee report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string",
                      "format": "date"
                    },
                    "to": {
                      "type": "string",
                      "format": "date"
                    },
                    "totalFeesUsd": {
                      "type": "number",
                      "description": "Total fees (base + markup)."
                    },
                    "baseFeeUsd": {
                      "type": "number"
                    },
                    "markupFeeUsd": {
                      "type": "number"
                    },
                    "breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "subAccountId": {
                            "type": "string"
                          },
                          "feesUsd": {
                            "type": "number"
                          },
                          "trades": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listFees"
      }
    },
    "/report/revenue": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Report — Revenue",
        "description": "Returns your net revenue (markup fees) across all sub-accounts for the given period.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/report/revenue?from=2026-01-01\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/report/revenue?from=2026-01-01',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { totalRevenueUsd } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/report/revenue',\n  params={'from': '2026-01-01'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Revenue report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string",
                      "format": "date"
                    },
                    "to": {
                      "type": "string",
                      "format": "date"
                    },
                    "totalRevenueUsd": {
                      "type": "number"
                    },
                    "monthly": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "month": {
                            "type": "string",
                            "example": "2026-01"
                          },
                          "revenueUsd": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listRevenue"
      }
    },
    "/report/invoice": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Report — Invoice",
        "description": "Returns invoiceable fee data for a billing period, formatted for your finance team. Includes base fees billed by MyStocks and markup collected by you.",
        "parameters": [
          {
            "name": "month",
            "in": "query",
            "required": true,
            "description": "Billing month in YYYY-MM format.",
            "schema": {
              "type": "string",
              "example": "2026-04"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/report/invoice?month=2026-04\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/report/invoice?month=2026-04',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst invoice = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/report/invoice',\n  params={'month': '2026-04'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "month": {
                      "type": "string"
                    },
                    "invoiceNumber": {
                      "type": "string"
                    },
                    "totalTrades": {
                      "type": "integer"
                    },
                    "grossVolumeUsd": {
                      "type": "number"
                    },
                    "baseFeeUsd": {
                      "type": "number"
                    },
                    "markupFeeUsd": {
                      "type": "number"
                    },
                    "netPayableUsd": {
                      "type": "number",
                      "description": "Amount owed to MyStocks for this period."
                    },
                    "dueDate": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listInvoice"
      }
    },
    "/report/reconciliation": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Report - Reconciliation suite",
        "description": "Daily reconciliation pack for B2B operations. Returns cash ledger, securities ledger, unsettled trades, fees, dividends, corporate actions, and custody position files for the partner master account and all sub-accounts. Add `format=csv&section=...` to download a section as CSV for finance, SFTP mirroring, or data warehouse ingestion. JSON responses include an explicit cash roll-forward, data-quality controls, and customer-sub-ledger-versus-MyStocks-omnibus proof. A status of `EXCEPTIONS` identifies unit or invalid-value differences; `INCOMPLETE` means no MyStocks omnibus positions were available for comparison. Sandbox returns a deterministic balanced fixture.\n",
        "parameters": [
          {
            "name": "asOf",
            "in": "query",
            "description": "Single business date to reconcile. Overrides default today when from/to are omitted.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-06-30"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            }
          },
          {
            "name": "section",
            "in": "query",
            "description": "CSV export section.",
            "schema": {
              "type": "string",
              "enum": [
                "summary",
                "cash",
                "securities",
                "unsettled",
                "fees",
                "dividends",
                "corporate_actions",
                "custody_positions"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Per-account transaction/order scan limit.",
            "schema": {
              "type": "integer",
              "default": 1000,
              "maximum": 5000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation report or CSV file.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "partnerKey": {
                      "type": "string"
                    },
                    "period": {
                      "type": "object",
                      "properties": {
                        "from": {
                          "type": "string",
                          "format": "date"
                        },
                        "to": {
                          "type": "string",
                          "format": "date"
                        }
                      }
                    },
                    "generatedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "exportManifest": {
                      "type": "object",
                      "description": "Pointers to the JSON and per-section CSV variants of this report.",
                      "properties": {
                        "api": {
                          "type": "string",
                          "example": "/api/v1/partner/report/reconciliation"
                        },
                        "csv": {
                          "type": "string",
                          "example": "/api/v1/partner/report/reconciliation?format=csv&section={cash|securities|unsettled|fees|dividends|corporate_actions|custody_positions}"
                        },
                        "sftpEligible": {
                          "type": "boolean"
                        }
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "accountCount": {
                          "type": "integer"
                        },
                        "closingCashBalance": {
                          "type": "number"
                        },
                        "closingSecuritiesValue": {
                          "type": "number"
                        },
                        "unsettledTradeCount": {
                          "type": "integer"
                        },
                        "unsettledBuyAmount": {
                          "type": "number"
                        },
                        "unsettledSellAmount": {
                          "type": "number"
                        },
                        "totalFees": {
                          "type": "number"
                        },
                        "totalDividends": {
                          "type": "number"
                        },
                        "custodyPositionCount": {
                          "type": "integer"
                        },
                        "corporateActionCount": {
                          "type": "integer"
                        },
                        "reconciliationStatus": {
                          "type": "string",
                          "enum": [
                            "BALANCED",
                            "EXCEPTIONS",
                            "INCOMPLETE"
                          ]
                        },
                        "custodyExceptionCount": {
                          "type": "integer"
                        }
                      }
                    },
                    "proof": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "BALANCED",
                            "EXCEPTIONS",
                            "INCOMPLETE"
                          ]
                        },
                        "cashRollForward": {
                          "type": "object",
                          "properties": {
                            "closingCashBalance": {
                              "type": "number"
                            },
                            "completedCredits": {
                              "type": "number"
                            },
                            "completedDebits": {
                              "type": "number"
                            },
                            "netMovement": {
                              "type": "number"
                            },
                            "derivedOpeningCashBalance": {
                              "type": "number"
                            },
                            "equation": {
                              "type": "string"
                            }
                          }
                        },
                        "custody": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "BALANCED",
                                "EXCEPTIONS",
                                "INCOMPLETE"
                              ]
                            },
                            "comparedInstrumentCount": {
                              "type": "integer"
                            },
                            "beneficialUnits": {
                              "type": "number"
                            },
                            "custodyUnits": {
                              "type": "number"
                            },
                            "exceptions": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "instrumentId": {
                                    "type": "string"
                                  },
                                  "beneficialUnits": {
                                    "type": "number"
                                  },
                                  "custodyUnits": {
                                    "type": "number"
                                  },
                                  "differenceUnits": {
                                    "type": "number"
                                  }
                                }
                              }
                            }
                          }
                        },
                        "notes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "accounts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationAccount"
                      }
                    },
                    "cashLedger": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationCashEntry"
                      }
                    },
                    "securitiesLedger": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationSecurityPosition"
                      }
                    },
                    "unsettledTrades": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationUnsettledTrade"
                      }
                    },
                    "fees": {
                      "type": "array",
                      "description": "Cash-ledger entries with type FEE.",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationCashEntry"
                      }
                    },
                    "dividends": {
                      "type": "array",
                      "description": "Cash-ledger entries with type DISTRIBUTION or DIVIDEND.",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationCashEntry"
                      }
                    },
                    "corporateActions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationCorporateAction"
                      }
                    },
                    "custodyPositions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationCustodyPosition"
                      }
                    }
                  }
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "operationId": "getReconciliationReport"
      }
    },
    "/topup": {
      "get": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "List top-up requests",
        "description": "Returns all master wallet top-up requests and their approval status.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/topup \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/topup', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { topups } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/topup',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Top-up request list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "topups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "number"
                          },
                          "paymentReference": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "approved",
                              "rejected"
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listTopup"
      },
      "post": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "Request top-up",
        "description": "Notifies MyStocks that you have sent a wire transfer to top up your master wallet.\nAn admin confirms the wire receipt and credits your wallet (typically within 1 business day).\nProvide your wire transfer reference in `paymentReference` so we can match the incoming funds.\n`paymentReference` is unique per partner after normalization; duplicate references return 409.\n\n**Sandbox**: the request auto-approves instantly — the master wallet is credited in the same call\nand `topup.confirmed` fires, so the funding webhook path is fully testable. Production requests\nstay `PENDING` until the remittance is confirmed.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopupRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/topup \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: topup_wire_2026_05_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"amount\":10000,\"paymentReference\":\"WIRE-2026-05-001\",\"paymentMethod\":\"SWIFT wire\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/topup', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'topup_wire_2026_05_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ amount: 10000, paymentReference: 'WIRE-2026-05-001', paymentMethod: 'SWIFT wire' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/v1/partner/topup',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'topup_wire_2026_05_001'},\n  json={'amount': 10000, 'paymentReference': 'WIRE-2026-05-001', 'paymentMethod': 'SWIFT wire'})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Top-up request received.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "topupId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createTopup"
      }
    },
    "/payout": {
      "get": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "List payout requests",
        "description": "Returns all payout requests from your master wallet.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/payout \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/payout', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { payouts } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/payout',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Payout request list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payouts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "number"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "approved",
                              "rejected"
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPayout"
      },
      "post": {
        "tags": [
          "Fund Flow"
        ],
        "summary": "Request payout",
        "description": "Requests a cash withdrawal from your master wallet to a bank account.\nAn admin reviews and processes payouts within 2 business days.\nThe requested amount is reserved immediately from your wallet balance.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoutRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/payout \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: payout_2026_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"amount\":5000,\"bankDetails\":{\"bankName\":\"Equity Bank Kenya\",\"accountName\":\"ACME Fintech Ltd\",\"accountNumber\":\"0123456789\",\"swiftCode\":\"EQBLKENA\"}}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/payout', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'payout_2026_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    amount: 5000,\n    bankDetails: { bankName: 'Equity Bank Kenya', accountName: 'ACME Fintech Ltd', accountNumber: '0123456789', swiftCode: 'EQBLKENA' },\n  }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/v1/partner/payout',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'payout_2026_001'},\n  json={\n    'amount': 5000,\n    'bankDetails': {'bankName': 'Equity Bank Kenya', 'accountName': 'ACME Fintech Ltd', 'accountNumber': '0123456789', 'swiftCode': 'EQBLKENA'},\n  })\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Payout request submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "payoutId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Insufficient master wallet balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createPayout"
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Returns all registered webhook endpoints for your partner account.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/webhooks \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/webhooks', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { webhooks } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/webhooks',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookRegistration"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listWebhooks"
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Register webhook",
        "description": "Registers a new HTTPS webhook endpoint. MyStocks sends a POST to your URL for each subscribed event.\n\n**Verification** — every delivery includes an `x-mystocks-signature` header:\n`HMAC-SHA256(secret, rawBody)` hex-encoded. Verify it before processing.\n\n**Retries** — failed deliveries are retried with exponential back-off: up to 6 attempts total\n(immediate, then 5 s, 30 s, 5 min, 30 min, 2 h). Your endpoint must respond `2xx` within 8 seconds\nor the delivery attempt is marked failed.\n\n**Delivery semantics** — at-least-once. The same event can be delivered more than once (e.g. your\nendpoint responded slowly and the delivery was retried), and events are not guaranteed to arrive in\norder. Deduplicate by event id and treat handlers as idempotent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/webhooks \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: webhook_primary_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"url\":\"https://yourapp.com/webhooks/mystocks\",\"events\":[\"trade.settled\",\"deposit.confirmed\"],\"secret\":\"my-signing-secret-min-16-chars\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/webhooks', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'webhook_primary_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    url: 'https://yourapp.com/webhooks/mystocks',\n    events: ['trade.settled', 'deposit.confirmed'],\n    secret: 'my-signing-secret-min-16-chars',\n  }),\n});\nconst { id } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/webhooks',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'webhook_primary_001'},\n  json={\n    'url': 'https://yourapp.com/webhooks/mystocks',\n    'events': ['trade.settled', 'deposit.confirmed'],\n    'secret': 'my-signing-secret-min-16-chars',\n  })\nwebhook_id = r.json()['id']\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Webhook registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookRegistration"
                }
              }
            }
          }
        },
        "operationId": "createWebhooks"
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete webhook",
        "description": "Permanently removes a webhook registration. In-flight deliveries will still complete.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "wh_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE https://mystocks.africa/api/v1/partner/webhooks/wh_abc123 \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: webhook_delete_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/webhooks/wh_abc123', {\n  method: 'DELETE',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'webhook_delete_001' },\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.delete('https://mystocks.africa/api/v1/partner/webhooks/wh_abc123',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'webhook_delete_001'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Webhook deleted."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteWebhooks"
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Test webhook",
        "description": "Sends a signed synthetic `test.event` to the registered webhook URL. Use this to verify connectivity and HMAC signature verification before going live. This operation does not simulate business events such as order fills.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "wh_abc123"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/webhooks/wh_abc123/test \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: webhook_test_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/webhooks/wh_abc123/test', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'webhook_test_001' },\n});\nconst { success, eventId, httpStatus } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/webhooks/wh_abc123/test',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'webhook_test_001'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Test delivery result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "eventId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "webhookId": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "httpStatus": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "HTTP status your endpoint returned."
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "The registered endpoint could not be reached or returned a non-2xx response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "eventId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "webhookId": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "error": {
                      "type": "string"
                    },
                    "httpStatus": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createTest"
      }
    },
    "/webhooks/{id}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Webhook deliveries",
        "description": "Returns the delivery log for a webhook endpoint — each attempt, response code, and body.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "wh_abc123"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/webhooks/wh_abc123/deliveries?limit=20\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/webhooks/wh_abc123/deliveries',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { deliveries } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get(\n  'https://mystocks.africa/api/v1/partner/webhooks/wh_abc123/deliveries',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery log.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "deliveryId": {
                            "type": "string"
                          },
                          "event": {
                            "type": "string"
                          },
                          "statusCode": {
                            "type": [
                              "integer",
                              "null"
                            ]
                          },
                          "success": {
                            "type": "boolean"
                          },
                          "latencyMs": {
                            "type": [
                              "integer",
                              "null"
                            ]
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "deliveredAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listDeliveries"
      }
    },
    "/dividends/{symbol}/history": {
      "get": {
        "tags": [
          "Dividends"
        ],
        "summary": "Dividend history for a symbol",
        "description": "Returns the full dividend declaration history for a specific stock symbol, ordered by ex-dividend date descending (most recent first).",
        "parameters": [
          {
            "name": "symbol",
            "in": "path",
            "required": true,
            "description": "Stock ticker, e.g. DANGCEM.NG or SCOM.KE.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum records to return.",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/dividends/SCOM.KE/history?limit=10\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/dividends/SCOM.KE/history?limit=10',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { history } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/dividends/SCOM.KE/history',\n  params={'limit': 10}, headers={'x-api-key': 'pk_live_KEY'})\nhistory = r.json()['history']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Dividend declaration history for the symbol.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "symbol": {
                      "type": "string",
                      "example": "SCOM.KE"
                    },
                    "history": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "amount": {
                            "type": "number",
                            "description": "Dividend per share in the declaration currency."
                          },
                          "currency": {
                            "type": "string",
                            "example": "KES"
                          },
                          "exDividendDate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "recordDate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "paymentDate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string"
                          },
                          "createdAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getDividendHistory"
      }
    },
    "/dividends/calendar": {
      "get": {
        "tags": [
          "Dividends"
        ],
        "summary": "Dividend calendar",
        "description": "Returns dividend declarations for stocks held across the partner's sub-accounts. Pass all=true to include declarations for symbols that no sub-account currently holds.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter declarations by lifecycle status.",
            "schema": {
              "type": "string",
              "enum": [
                "DECLARED",
                "PAID",
                "CANCELLED",
                "ALL"
              ]
            }
          },
          {
            "name": "all",
            "in": "query",
            "description": "Return declarations for all symbols, including those not held by partner sub-accounts.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/dividends/calendar?status=DECLARED\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/dividends/calendar?status=DECLARED',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { declarations } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/dividends/calendar',\n  params={'status': 'DECLARED'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Dividend declarations visible to this partner.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "declarations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "exDividendDate": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "recordDate": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "paymentDate": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "partnerEligible": {
                            "type": "boolean"
                          },
                          "createdAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listCalendar"
      }
    },
    "/report/dividends": {
      "get": {
        "tags": [
          "Dividends"
        ],
        "summary": "Report — Dividends",
        "description": "Aggregated dividend distributions paid to sub-accounts for a given period.\nReturns totals, a per-symbol breakdown, and a per-sub-account breakdown.\nAdd `?format=csv` to download a spreadsheet of the by-symbol totals.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Start of period (ISO date, e.g. 2026-01-01). Defaults to 365 days ago."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "End of period (ISO date). Defaults to today."
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv"
              ]
            },
            "description": "Pass `csv` to receive a downloadable spreadsheet instead of JSON."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/report/dividends?from=2026-01-01\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/report/dividends?from=2026-01-01',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { totalUsdReceived, bySymbol, bySubAccount } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/report/dividends',\n  params={'from': '2026-01-01'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Dividend distribution report (JSON) or CSV file.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "totalUsdReceived": {
                      "type": "number",
                      "description": "Total USD credited to sub-accounts as dividends in the period."
                    },
                    "distributionCount": {
                      "type": "integer",
                      "description": "Number of individual distribution transactions."
                    },
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "bySymbol": {
                      "type": "array",
                      "description": "Totals grouped by stock symbol.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "totalUsd": {
                            "type": "number"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "bySubAccount": {
                      "type": "array",
                      "description": "Totals grouped by sub-account.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "subAccountId": {
                            "type": "string"
                          },
                          "externalId": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "displayName": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "totalUsd": {
                            "type": "number"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getDividendReport"
      }
    },
    "/api-keys/data-key": {
      "get": {
        "tags": [
          "Key Management"
        ],
        "summary": "Get data API key",
        "description": "Returns the current read-only data API key (if issued). Data keys can access market data endpoints but cannot execute trades or move funds.\n\n⚠️ **Prefer server-side use.** An embedded `pk_data_` key can be extracted from any client bundle, shares the parent full key's rate-limit bucket, and rotating it breaks every shipped install (only one is active per partner). For production apps, proxy market data through your backend or use short-lived tokens from `POST /oauth/token`; reserve embedded data keys for low-stakes surfaces such as public widgets.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/api-keys/data-key \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/api-keys/data-key', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { dataKey } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/api-keys/data-key',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Data API key info.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dataKey": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Read-only key starting with pk_data_. null if not yet issued."
                    },
                    "keyId": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Internal identifier for the active data key. null if none is active."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listDataKey"
      },
      "post": {
        "tags": [
          "Key Management"
        ],
        "summary": "Issue data API key",
        "description": "Issues a new read-only data API key. Only one data key can be active at a time — calling this again rotates the existing key.\n\n⚠️ **Prefer server-side use.** An embedded `pk_data_` key can be extracted from any client bundle, shares the parent full key's rate-limit bucket, and rotating it (or calling this endpoint again) breaks every shipped install (only one is active per partner). For production apps, proxy market data through your backend or use short-lived tokens from `POST /oauth/token`; reserve embedded data keys for low-stakes surfaces such as public widgets.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-sandbox": false,
        "x-sandbox-stub": true,
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/api-keys/data-key \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: data_key_create_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/api-keys/data-key', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'data_key_create_001' },\n});\nconst { dataKey } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/api-keys/data-key',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'data_key_create_001'})\ndata_key = r.json()['dataKey']\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Data key issued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dataKey": {
                      "type": "string",
                      "example": "pk_data_a1b2c3d4e5f6"
                    },
                    "note": {
                      "type": "string",
                      "description": "One-time display and client-embedding security guidance."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createDataKey"
      },
      "delete": {
        "tags": [
          "Key Management"
        ],
        "summary": "Revoke data API key",
        "description": "Permanently revokes the data API key. Market data endpoints will return 401 until a new key is issued.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-sandbox": false,
        "x-sandbox-stub": true,
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X DELETE https://mystocks.africa/api/v1/partner/api-keys/data-key \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: data_key_delete_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/api-keys/data-key', {\n  method: 'DELETE',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'data_key_delete_001' },\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.delete('https://mystocks.africa/api/v1/partner/api-keys/data-key',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'data_key_delete_001'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Data key revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Data key revoked."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "deleteDataKey"
      }
    },
    "/api-keys/rotate": {
      "post": {
        "tags": [
          "Key Management"
        ],
        "summary": "Rotate API key",
        "description": "Issues a new primary API key and immediately suspends the current credential. Partner data remains attached to\nthe same stable partner identity. The new key is returned once in `newApiKey`; store it securely before closing\nthe response. There is no overlap window.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirm"
                ],
                "properties": {
                  "confirm": {
                    "type": "boolean",
                    "enum": [
                      true
                    ],
                    "description": "Explicit acknowledgement that the current key will stop working immediately."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/api-keys/rotate \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: api_key_rotate_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"confirm\":true}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/api-keys/rotate', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'api_key_rotate_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ confirm: true }),\n});\nconst { newApiKey } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/api-keys/rotate',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'api_key_rotate_001'},\n  json={'confirm': True})\nnew_key = r.json()['newApiKey']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Key rotated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "newApiKey": {
                      "type": "string",
                      "description": "New primary API key. Store immediately — shown once."
                    },
                    "oldApiKey": {
                      "type": "string",
                      "description": "Masked form of the credential that was suspended."
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createRotate"
      }
    },
    "/api-keys/revoke": {
      "post": {
        "tags": [
          "Key Management"
        ],
        "summary": "Revoke API key",
        "description": "Permanently revokes a specific API key. Use this to invalidate a compromised key immediately. If you revoke your only active key, you must contact support to restore access.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "security": [
          {
            "FirebaseToken": []
          }
        ],
        "x-sandbox": false,
        "x-sandbox-stub": true,
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevokeKeyRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/api-keys/revoke \\\n  -H \"Authorization: Bearer FIREBASE_ID_TOKEN\" \\\n  -H \"Idempotency-Key: api_key_revoke_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"apiKey\":\"pk_live_a1b2c3...\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/api-keys/revoke', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer FIREBASE_ID_TOKEN', 'Idempotency-Key': 'api_key_revoke_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ apiKey: 'pk_live_a1b2c3...' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/v1/partner/api-keys/revoke',\n  headers={'Authorization': 'Bearer FIREBASE_ID_TOKEN', 'Idempotency-Key': 'api_key_revoke_001'},\n  json={'apiKey': 'pk_live_a1b2c3...'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Key revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "API key revoked."
                    },
                    "apiKey": {
                      "type": "string",
                      "description": "Masked form of the revoked credential."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Key not found or does not belong to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createRevoke"
      }
    },
    "/audit": {
      "get": {
        "tags": [
          "Observability"
        ],
        "summary": "Audit log",
        "description": "Returns a cursor-paginated list of API calls made with your live key, ordered newest first.\nEach entry includes the endpoint path, HTTP method, source IP, user-agent, and timestamp.\nUse `?limit=` and `?cursor=` for pagination (see the Pagination section).\n",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor from the previous page's nextCursor field.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "before",
            "in": "query",
            "description": "ISO timestamp — return only entries older than this value.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/audit?limit=50\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/audit?limit=50',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { entries, hasMore, nextCursor } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/audit',\n  params={'limit': 50}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated audit log entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "endpoint": {
                            "type": "string",
                            "example": "/api/v1/partner/users/usr_abc/trade"
                          },
                          "method": {
                            "type": "string",
                            "example": "POST"
                          },
                          "ip": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "userAgent": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "timestamp": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listAudit"
      }
    },
    "/usage": {
      "get": {
        "tags": [
          "Observability"
        ],
        "summary": "Usage analytics",
        "description": "Returns daily API call totals for the last N days (default 30, max 90), aggregated from per-minute rate-limit windows.\nAlso returns the current rate-limit window status so you can see remaining capacity in real time.\n",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "Number of days to look back. Default 30, max 90.",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 90
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/usage?days=30\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/usage?days=30', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { totalCalls, daily, currentWindow, tier } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/usage',\n  params={'days': 30}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage analytics and current rate-limit window.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keyId": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string",
                      "example": "starter"
                    },
                    "periodDays": {
                      "type": "integer",
                      "example": 30
                    },
                    "totalCalls": {
                      "type": "integer",
                      "description": "Total API calls in the requested period."
                    },
                    "daily": {
                      "type": "array",
                      "description": "One entry per calendar day, ordered oldest first.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date",
                            "example": "2026-05-01"
                          },
                          "calls": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "currentWindow": {
                      "type": "object",
                      "description": "Live rate-limit window status.",
                      "properties": {
                        "limit": {
                          "type": "integer",
                          "description": "Maximum requests allowed per minute for your tier."
                        },
                        "remaining": {
                          "type": "integer",
                          "description": "Requests remaining in the current 1-minute window."
                        },
                        "resetAt": {
                          "type": "string",
                          "format": "date-time",
                          "description": "When the current window resets."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listUsage"
      }
    },
    "/sla": {
      "get": {
        "tags": [
          "SLA"
        ],
        "summary": "SLA status",
        "description": "Returns real-time service health, active incidents, upcoming maintenance windows, and your SLA tier commitments.\n\n| Tier | Uptime SLO | Rate Limit |\n|------|-----------|------------|\n| Standard (starter) | 99.5% | 100 req/min |\n| Professional (growth) | 99.9% | 500 req/min |\n| Enterprise | 99.95% | 2,000 req/min |\n",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/sla \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/sla', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { overallStatus, services, activeIncidents, partner } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/sla',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Service health, incidents, and partner SLA commitments.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "overallStatus": {
                      "type": "string",
                      "enum": [
                        "operational",
                        "partial_outage",
                        "major_outage"
                      ],
                      "description": "Platform-wide health summary."
                    },
                    "checkedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "services": {
                      "type": "array",
                      "description": "Per-service health breakdown.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "trading-api"
                          },
                          "name": {
                            "type": "string",
                            "example": "Trading API"
                          },
                          "description": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "operational",
                              "degraded",
                              "outage"
                            ]
                          },
                          "uptime30d": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "uptime90d": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "currentMonthUptime": {
                            "type": [
                              "number",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "activeIncidents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string",
                            "enum": [
                              "P0",
                              "P1",
                              "P2",
                              "P3"
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "affectedServices": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "startedAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "resolvedAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "pastIncidents": {
                      "type": "array",
                      "description": "Incidents resolved in the last 30 days.",
                      "items": {
                        "type": "object"
                      }
                    },
                    "maintenance": {
                      "type": "array",
                      "description": "Upcoming scheduled maintenance windows.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "scheduledStart": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "scheduledEnd": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "partner": {
                      "type": "object",
                      "description": "Your SLA tier and committed SLO targets.",
                      "properties": {
                        "slaTier": {
                          "type": "string",
                          "example": "Standard"
                        },
                        "slo": {
                          "type": "object",
                          "properties": {
                            "uptimeSlo": {
                              "type": "number",
                              "example": 99.5
                            },
                            "responseP0": {
                              "type": "string",
                              "example": "4h"
                            },
                            "responseP1": {
                              "type": "string",
                              "example": "8h"
                            },
                            "responseP2": {
                              "type": "string",
                              "example": "24h"
                            },
                            "responseP3": {
                              "type": "string",
                              "example": "72h"
                            },
                            "credits": {
                              "type": "number",
                              "description": "SLA credit percentage on breach."
                            }
                          }
                        },
                        "executionSlo": {
                          "type": "object",
                          "properties": {
                            "marketOrderExecution": {
                              "type": "object",
                              "properties": {
                                "minMinutes": {
                                  "type": "integer",
                                  "example": 1
                                },
                                "maxMinutes": {
                                  "type": "integer",
                                  "example": 5
                                },
                                "appliesTo": {
                                  "type": "string"
                                },
                                "excludes": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "monitoringCronId": {
                                  "type": "string",
                                  "example": "execution-sla-monitor"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listSla"
      }
    },
    "/oauth/token": {
      "post": {
        "x-sandbox": false,
        "tags": [
          "Enterprise Security"
        ],
        "summary": "Issue OAuth client-credentials access token",
        "description": "Issues a short-lived Bearer token for partners that have OAuth client credentials enabled in their enterprise security policy. Provide the partner API key as the client secret via HTTP Basic auth, form body, or JSON body.\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Short-lived OAuth access token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthTokenResponse"
                }
              }
            }
          },
          "403": {
            "description": "OAuth client credentials are not enabled for this partner."
          }
        },
        "operationId": "createOAuthToken"
      }
    },
    "/security": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Enterprise Security"
        ],
        "summary": "Get enterprise security controls",
        "description": "Returns the active enterprise security policy for the authenticated partner key, including IP allowlists, signed-request requirements, mTLS gateway requirements, scoped-key governance, rotation policy, and security evidence metadata.\n",
        "responses": {
          "200": {
            "description": "Active enterprise security policy.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/EnterpriseSecurityPolicy"
                    },
                    "key": {
                      "type": "object",
                      "properties": {
                        "keyId": {
                          "type": "string"
                        },
                        "partnerId": {
                          "type": "string"
                        },
                        "keyType": {
                          "type": "string",
                          "enum": [
                            "full",
                            "data"
                          ]
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "requiredScopeForThisEndpoint": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "rotationDueAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    },
                    "signedRequestSpec": {
                      "type": "object",
                      "properties": {
                        "headers": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "algorithm": {
                          "type": "string",
                          "example": "HMAC-SHA256"
                        },
                        "payload": {
                          "type": "string",
                          "example": "timestamp.method.path.sha256(raw_request_body)"
                        },
                        "timestampToleranceSeconds": {
                          "type": "integer",
                          "example": 300
                        }
                      }
                    },
                    "mtls": {
                      "type": "object",
                      "properties": {
                        "acceptedGatewayHeaders": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getEnterpriseSecurity"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Enterprise Security"
        ],
        "summary": "Update enterprise security controls",
        "description": "Updates partner-managed enterprise controls. A full API key is required. Once signed requests or mTLS are enabled, subsequent write requests must satisfy the configured controls.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnterpriseSecurityUpdateRequest"
              },
              "examples": {
                "signedRequests": {
                  "value": {
                    "ipAllowlist": [
                      "203.0.113.10"
                    ],
                    "requireSignedRequests": true,
                    "keyRotationDays": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated enterprise security policy.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "policy": {
                      "$ref": "#/components/schemas/EnterpriseSecurityPolicy"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "updateEnterpriseSecurity"
      }
    },
    "/certification": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Certification"
        ],
        "summary": "Get certification checklist",
        "description": "Returns the sandbox-to-production checklist covering golden-path tests, concurrency/idempotency tests, webhook retry tests, failover drills, reconciliation exports, enterprise security, and MyStocks go-live approval.\n",
        "responses": {
          "200": {
            "description": "Certification progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificationResponse"
                }
              }
            }
          }
        },
        "operationId": "getCertification"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Certification"
        ],
        "summary": "Update certification evidence",
        "description": "Updates partner-owned certification checks with status, evidence URL, and notes. MyStocks-owned approval checks cannot be marked passed or waived by partners.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CertificationUpdateRequest"
              },
              "examples": {
                "webhookRetry": {
                  "value": {
                    "checks": {
                      "webhook_retry": {
                        "status": "passed",
                        "evidenceUrl": "https://trust.partner.example/mystocks/webhook-retry.pdf",
                        "notes": "Replay and retry tests passed in sandbox."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated certification progress.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/CertificationResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "operationId": "updateCertification"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Certification"
        ],
        "summary": "Request go-live review",
        "description": "Requests MyStocks operations review for production go-live approval after certification evidence is attached.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "request_go_live"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Go-live review requested.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/CertificationResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        },
        "operationId": "requestGoLiveReview"
      }
    },
    "/documents": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "List document templates and generation history",
        "description": "Returns the partner document catalogue and the 20 most recent generated-document evidence records. Sandbox entries include downloadable deterministic PDF fixtures.",
        "responses": {
          "200": {
            "description": "Document catalogue and recent generation history.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "catalog",
                    "recent"
                  ],
                  "properties": {
                    "catalog": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "title",
                          "description",
                          "requiresUser"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "account-statement",
                              "compliance-summary",
                              "operations-summary"
                            ]
                          },
                          "title": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "requiresUser": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "recent": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "userId": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "createdBy": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "createdAt": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPartnerDocuments"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Reports"
        ],
        "summary": "Generate a partner PDF document",
        "description": "Generates an account statement, compliance summary, or partner-wide operations summary and retains an auditable generation record. Customer documents require a partner-owned sub-account userId.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "account-statement",
                      "compliance-summary",
                      "operations-summary"
                    ]
                  },
                  "userId": {
                    "type": "string",
                    "description": "Required for account-statement and compliance-summary."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated PDF document.",
            "headers": {
              "X-Document-Id": {
                "description": "Evidence record identifier for the generated document.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported document type or missing required userId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The requested customer does not belong to the partner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "generatePartnerDocument"
      }
    },
    "/notification-rules": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Settings"
        ],
        "summary": "Get notification policy",
        "description": "Returns notification rules plus the supported event and delivery-channel catalogues.",
        "responses": {
          "200": {
            "description": "Current notification policy.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "rules",
                    "availableEvents",
                    "availableChannels"
                  ],
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PartnerNotificationRule"
                      }
                    },
                    "updatedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "updatedBy": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "availableEvents": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "availableChannels": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "email",
                          "webhook",
                          "sms"
                        ]
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getNotificationRules"
      },
      "put": {
        "x-sandbox": false,
        "tags": [
          "Settings"
        ],
        "summary": "Replace notification policy",
        "description": "Validates and replaces the partner notification rules. A maximum of 50 rules is accepted.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rules"
                ],
                "properties": {
                  "rules": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/PartnerNotificationRule"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Notification policy saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message",
                    "rules",
                    "updatedAt",
                    "updatedBy"
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PartnerNotificationRule"
                      }
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedBy": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Notification rule validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "replaceNotificationRules"
      }
    },
    "/reconciliation-signoffs": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Reports"
        ],
        "summary": "List reconciliation sign-offs",
        "description": "Returns up to 50 recent sign-offs owned by the authenticated partner.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation sign-off history.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "signoffs",
                    "count"
                  ],
                  "properties": {
                    "signoffs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReconciliationSignoff"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listReconciliationSignoffs"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Reports"
        ],
        "summary": "Record a reconciliation sign-off",
        "description": "Records partner evidence that a dated reconciliation period was reviewed.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "to",
                  "notes"
                ],
                "properties": {
                  "from": {
                    "type": "string",
                    "format": "date"
                  },
                  "to": {
                    "type": "string",
                    "format": "date"
                  },
                  "status": {
                    "type": "string",
                    "maxLength": 30
                  },
                  "exceptionCount": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "reportGeneratedAt": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "notes": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 4000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reconciliation sign-off recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message",
                    "signoff"
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "signoff": {
                      "$ref": "#/components/schemas/ReconciliationSignoff"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Dates or notes failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createReconciliationSignoff"
      }
    },
    "/settings": {
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Get settings",
        "description": "Returns the current partner configuration — logo URL, custom email config, and notification preferences.",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/settings \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/settings', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst settings = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/settings',\n  headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Partner settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "logoUrl": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "emailConfig": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Custom SMTP config. null if using MyStocks default emails."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listSettings"
      },
      "patch": {
        "tags": [
          "Settings"
        ],
        "summary": "Update settings",
        "description": "Updates partner configuration. All fields are optional — only provided fields are changed. Set `emailConfig` to `null` to revert to MyStocks default transactional emails.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettingsUpdateRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X PATCH https://mystocks.africa/api/v1/partner/settings \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: settings_update_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"logoUrl\":\"https://cdn.yourapp.com/logo.png\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/v1/partner/settings', {\n  method: 'PATCH',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'settings_update_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ logoUrl: 'https://cdn.yourapp.com/logo.png' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.patch('https://mystocks.africa/api/v1/partner/settings',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'settings_update_001'},\n  json={'logoUrl': 'https://cdn.yourapp.com/logo.png'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Settings updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Settings updated."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "updateSettings"
      },
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Test SMTP",
        "description": "Sends a test email via your saved custom SMTP configuration to the authenticated partner account email. Use this to verify that your email settings are correct before going live. This operation does not accept a request body.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/settings \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\n  -H \"Idempotency-Key: settings_smtp_test_001\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/settings', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', 'Idempotency-Key': 'settings_smtp_test_001' },\n});\nconst { delivered } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/settings',\n  headers={'Authorization': 'Bearer pk_live_KEY', 'Idempotency-Key': 'settings_smtp_test_001'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "SMTP test result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Test email sent to dev@acme.com"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createSettings"
      }
    },
    "/test-tools/deposit": {
      "post": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "Credit virtual funds",
        "description": "Credits virtual USD to a sandbox sub-account for deterministic integration testing. New integrations use the sandbox base with an `sk_sandbox_` key. Production-base access is restricted legacy compatibility for specifically approved partners.\nNo real money moves. Maximum $1,000,000 per call.\nTriggers a `deposit.confirmed` webhook if one is registered.\nFormerly `POST /sandbox/deposit`, which remains a deprecated alias.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxDepositRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/sandbox/v1/partner/test-tools/deposit \\\n  -H \"Authorization: Bearer sk_sandbox_KEY\" \\\n  -H \"Idempotency-Key: test_deposit_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"subAccountId\":\"usr_test_abc123\",\"amount\":10000,\"note\":\"Integration test deposit\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "await fetch('https://mystocks.africa/api/sandbox/v1/partner/test-tools/deposit', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer sk_sandbox_KEY', 'Idempotency-Key': 'test_deposit_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ subAccountId: 'usr_test_abc123', amount: 10000, note: 'Integration test' }),\n});\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nrequests.post('https://mystocks.africa/api/sandbox/v1/partner/test-tools/deposit',\n  headers={'Authorization': 'Bearer sk_sandbox_KEY', 'Idempotency-Key': 'test_deposit_001'},\n  json={'subAccountId': 'usr_test_abc123', 'amount': 10000, 'note': 'Integration test'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Virtual funds credited.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "walletBalance": {
                      "type": "number"
                    },
                    "transactionId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createTestToolsDeposit"
      }
    },
    "/sandbox/deposit": {
      "post": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "Credit virtual funds (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `POST /test-tools/deposit`. Existing approved legacy integrations keep working; new integrations use `/test-tools/deposit` on the sandbox base with an `sk_sandbox_` key.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxDepositRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Virtual funds credited.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "walletBalance": {
                      "type": "number"
                    },
                    "transactionId": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createSandboxDeposit"
      }
    },
    "/test-tools/trade": {
      "post": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "Submit test trade",
        "description": "Places a deterministic test trade without hitting a real exchange.\n\n**Recommended:** use the sandbox base URL with an `sk_sandbox_` key. Set `outcome` to\n`PENDING`, `FILL`, `PARTIAL_FILL`, `REJECT`, `CANCEL`, or `FAIL_SETTLEMENT`; sandbox wallet\nreservations, holdings, execution records, and webhooks are updated consistently. A pending\nor partially filled order can be advanced with `PATCH /test-tools/orders/{orderId}`.\n\nThe production base retains the older live-key integration tool for compatibility:\n\n**Cheat codes** for deterministic outcomes:\n| Quantity | Outcome |\n|----------|---------|\n| `100` | Auto-fills immediately (triggers `trade.settled`) |\n| `999` | Auto-rejects immediately (triggers `trade.rejected`) |\n| Any other | Enters PENDING state — use admin panel to settle |\n\nFormerly `POST /sandbox/trade`, which remains a supported alias.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxTradeRequest"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "# Auto-fill trade (quantity=100)\ncurl -X POST https://mystocks.africa/api/sandbox/v1/partner/test-tools/trade \\\n  -H \"Authorization: Bearer sk_sandbox_KEY\" \\\n  -H \"Idempotency-Key: test_trade_001\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"symbol\":\"SCOM.KE\",\"type\":\"BUY\",\"quantity\":100,\"subAccountId\":\"usr_test_abc123\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "// quantity=100 auto-fills; quantity=999 auto-rejects\nconst res = await fetch('https://mystocks.africa/api/sandbox/v1/partner/test-tools/trade', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer sk_sandbox_KEY', 'Idempotency-Key': 'test_trade_001', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ symbol: 'SCOM.KE', type: 'BUY', quantity: 100, subAccountId: 'usr_test_abc123' }),\n});\nconst { orderId, status } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\n# quantity=100 = auto-fill, quantity=999 = auto-reject\nr = requests.post('https://mystocks.africa/api/sandbox/v1/partner/test-tools/trade',\n  headers={'Authorization': 'Bearer sk_sandbox_KEY', 'Idempotency-Key': 'test_trade_001'},\n  json={'symbol': 'SCOM.KE', 'type': 'BUY', 'quantity': 100, 'subAccountId': 'usr_test_abc123'})\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Test order placed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          }
        },
        "operationId": "createTestToolsTrade"
      }
    },
    "/test-tools/orders/{orderId}": {
      "patch": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "Advance a sandbox test order",
        "description": "Sandbox-only deterministic execution control. Advance an isolated `PENDING` or `PARTIALLY_FILLED` test order through fill, partial fill, rejection, cancellation, or settlement failure. A fully filled order can be marked settled. The operation updates virtual wallet reservations, holdings, execution records, and signed webhooks without contacting an exchange or requiring MyStocks staff.",
        "x-sandbox-only": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subAccountId",
                  "action"
                ],
                "properties": {
                  "subAccountId": {
                    "type": "string"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "FILL",
                      "PARTIAL_FILL",
                      "REJECT",
                      "CANCEL",
                      "SETTLE",
                      "FAIL_SETTLEMENT"
                    ]
                  },
                  "quantity": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "description": "Required for PARTIAL_FILL and must be less than the remaining quantity."
                  },
                  "unitPriceUsd": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "description": "Optional deterministic fill price; defaults to the order price."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Simulator transition applied."
          },
          "400": {
            "description": "Invalid simulator action or request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Sub-account or simulator order not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Transition conflicts with the current order state or reserved balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateTestToolsOrder"
      }
    },
    "/sandbox/trade": {
      "post": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "Submit test trade (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `POST /test-tools/trade`. Existing approved legacy integrations keep working; new integrations use `/test-tools/trade` on the sandbox base with an `sk_sandbox_` key.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SandboxTradeRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Test order placed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          }
        },
        "operationId": "createSandboxTrade"
      }
    },
    "/test-tools/orders": {
      "get": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "List test orders",
        "description": "Returns all test orders created via the test trade endpoint. Formerly `GET /sandbox/orders`, which remains a supported alias.\n",
        "parameters": [
          {
            "name": "subAccountId",
            "in": "query",
            "description": "Filter by sub-account ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "WORKING",
                "PARTIALLY_FILLED",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED",
                "EXPIRED"
              ],
              "description": "FILLED is canonical. COMPLETED and PROCESSING are legacy compatibility aliases."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/sandbox/v1/partner/test-tools/orders?status=FILLED\" \\\n  -H \"x-api-key: sk_sandbox_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/sandbox/v1/partner/test-tools/orders?status=FILLED',\n  { headers: { 'x-api-key': 'sk_sandbox_KEY' } }\n);\nconst { orders } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/sandbox/v1/partner/test-tools/orders',\n  params={'status': 'FILLED'}, headers={'x-api-key': 'sk_sandbox_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Test order list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listTestToolsOrders"
      }
    },
    "/sandbox/orders": {
      "get": {
        "tags": [
          "Integration Test Tools"
        ],
        "summary": "List test orders (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `GET /test-tools/orders`. Existing approved legacy integrations keep working; new integrations use `/test-tools/orders` on the sandbox base with an `sk_sandbox_` key.\n",
        "parameters": [
          {
            "name": "subAccountId",
            "in": "query",
            "description": "Filter by sub-account ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "WORKING",
                "PARTIALLY_FILLED",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED",
                "EXPIRED"
              ],
              "description": "FILLED is canonical. COMPLETED and PROCESSING are legacy compatibility aliases."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test order list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listSandboxOrders"
      }
    },
    "/market/movers": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Top gainers & losers",
        "description": "Returns the top price movers (gainers or losers) for a given exchange, sorted by percentage change.\nResults are paginated (meta uses camelCase `totalCount`/`perPage`/`hasNext`; the snake_case aliases are\ndeprecated, removal 2026-10-01). Also available at `GET /market-data/movers` (deprecated alias, identical\nhandler).\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "required": true,
            "description": "Exchange code (NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM).",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "\"gainers\" for top price increases, \"losers\" for top price decreases. Default: gainers.",
            "schema": {
              "type": "string",
              "enum": [
                "gainers",
                "losers"
              ],
              "default": "gainers"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results to return. Default 10, max 100.",
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/market/movers?exchange=NSE&direction=gainers&limit=10\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/market/movers?exchange=NSE&direction=gainers',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data, meta } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market/movers',\n  params={'exchange': 'NSE', 'direction': 'gainers', 'limit': 10},\n  headers={'x-api-key': 'pk_live_KEY'})\nmovers = r.json()['data']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Top movers for the requested exchange and direction.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StockSummary"
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Meta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "direction": {
                              "type": "string",
                              "enum": [
                                "gainers",
                                "losers"
                              ]
                            },
                            "page": {
                              "type": "integer"
                            },
                            "totalCount": {
                              "type": "integer",
                              "description": "Total number of stocks with price change data on this exchange."
                            },
                            "perPage": {
                              "type": "integer"
                            },
                            "hasNext": {
                              "type": "boolean"
                            },
                            "total_count": {
                              "type": "integer",
                              "deprecated": true,
                              "description": "Deprecated alias of totalCount — removed 2026-10-01."
                            },
                            "per_page": {
                              "type": "integer",
                              "deprecated": true,
                              "description": "Deprecated alias of perPage — removed 2026-10-01."
                            },
                            "has_next": {
                              "type": "boolean",
                              "deprecated": true,
                              "description": "Deprecated alias of hasNext — removed 2026-10-01."
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMarketMovers"
      }
    },
    "/market-data/movers": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Top gainers & losers (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `GET /market/movers`. Identical handler and response shape. Existing integrations keep working; use `/market/movers` in new code.",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "gainers",
                "losers"
              ],
              "default": "gainers"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "See `GET /market/movers`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StockSummary"
                }
              }
            }
          }
        },
        "operationId": "getMarketDataMovers"
      }
    },
    "/market-data/quotes": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Get quotes — single or batch (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `GET /market/quotes`. Identical handler and response shape. Existing integrations keep working; use `/market/quotes` in new code.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "symbols",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "SCOM.KE,GLD.ZA"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "See `GET /market/quotes`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "operationId": "getMarketDataQuotes"
      }
    },
    "/market/exchanges": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "List supported exchanges",
        "description": "Returns metadata for all supported exchanges — exchange code, name, country, currency, timezone, local trading hours, and real-time market status (OPEN/CLOSED).\nNo parameters required. Response reflects the current server time.\nFormerly `GET /market-data/exchanges`, which remains a supported (deprecated) alias.\n",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/market/exchanges \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/market/exchanges', {\n  headers: { 'x-api-key': 'pk_live_KEY' },\n});\nconst { data, meta } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market/exchanges',\n  headers={'x-api-key': 'pk_live_KEY'})\nexchanges = r.json()['data']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "List of supported exchanges with current market status.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "description": "Exchange code.",
                            "example": "NSE"
                          },
                          "mic": {
                            "type": "string",
                            "description": "ISO 10383 operating Market Identifier Code.",
                            "example": "XNAI"
                          },
                          "name": {
                            "type": "string",
                            "description": "Full exchange name.",
                            "example": "Nairobi Securities Exchange"
                          },
                          "country": {
                            "type": "string",
                            "description": "ISO 3166-1 alpha-2 country code.",
                            "example": "KE"
                          },
                          "currency": {
                            "type": "string",
                            "description": "Local trading currency.",
                            "example": "KES"
                          },
                          "timezone": {
                            "type": "string",
                            "description": "IANA timezone identifier.",
                            "example": "Africa/Nairobi"
                          },
                          "trading_hours": {
                            "type": "object",
                            "properties": {
                              "open": {
                                "type": "string",
                                "description": "Local session open time (HH:MM).",
                                "example": "09:00"
                              },
                              "close": {
                                "type": "string",
                                "description": "Local session close time (HH:MM).",
                                "example": "15:00"
                              }
                            }
                          },
                          "settlement": {
                            "type": "object",
                            "nullable": true,
                            "description": "Exchange settlement cycle. See GET /market/settlement for full detail (CSD, processing window).",
                            "properties": {
                              "cycle": {
                                "type": "string",
                                "description": "Settlement cycle in T+N notation.",
                                "example": "T+3"
                              },
                              "cycleDays": {
                                "type": "integer",
                                "description": "Settlement cycle length in business days.",
                                "example": 3
                              }
                            }
                          },
                          "market_status": {
                            "type": "string",
                            "enum": [
                              "OPEN",
                              "CLOSED"
                            ],
                            "description": "Current market status based on server time and exchange schedule."
                          }
                        }
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMarketExchanges"
      }
    },
    "/market-data/exchanges": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "List supported exchanges (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `GET /market/exchanges` — same handler and response shape. Existing integrations keep working; use `/market/exchanges` in new code.",
        "responses": {
          "200": {
            "description": "See `GET /market/exchanges`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "operationId": "getMarketDataExchanges"
      }
    },
    "/market/snapshot": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Market snapshot (quote + daily bar + previous daily bar + intraday sample)",
        "description": "Returns a one-call bundle for one or many symbols: the delayed `quote` (same shape as `/market/quotes`, carrying `asOf`, `stale`, and `dataQuality`), today's `dailyBar`, the `prevDailyBar`, and the latest coarse `intradayBar` sample. Replaces the 3–4 round-trips a dashboard would otherwise make.\n\nSingle mode requires both `symbol` and `exchange`; batch mode takes `symbols` (comma-separated, ≤50), returning a map of symbol → snapshot plus `not_found`.\n\n`dailyBar` is derived from the latest 15-minute-delayed observation until the end-of-day pass writes today's candle. `intradayBar` is `null` until the 3×/day intraday capture has run for the symbol.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "description": "Single-symbol mode. Use together with `exchange`.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "description": "Exchange code (required in single-symbol mode).",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "symbols",
            "in": "query",
            "description": "Batch mode. Comma-separated exchange-qualified tickers (max 50), e.g. `SCOM.KE,MTN.ZA`.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE,MTN.ZA"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/market/snapshot?symbols=SCOM.KE,MTN.ZA\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/market/snapshot?symbols=SCOM.KE,MTN.ZA',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data, not_found } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market/snapshot',\n  params={'symbols': 'SCOM.KE,MTN.ZA'},\n  headers={'x-api-key': 'pk_live_KEY'})\nsnapshots = r.json()['data']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot bundle. Single mode returns one snapshot under `data`; batch mode returns a symbol→snapshot map.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "description": "Single mode: one Snapshot object. Batch mode: a map of symbol → Snapshot.\n",
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Snapshot"
                        },
                        {
                          "type": "object",
                          "additionalProperties": {
                            "$ref": "#/components/schemas/Snapshot"
                          }
                        }
                      ]
                    },
                    "not_found": {
                      "type": "array",
                      "description": "Batch mode only — requested symbols that could not be resolved.",
                      "items": {
                        "type": "string"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (missing params, invalid symbol, batch > 50).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Single-symbol mode — symbol not found on the specified exchange.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMarketSnapshot"
      }
    },
    "/market/ohlcv": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "OHLCV price history (end-of-day candles)",
        "description": "Returns end-of-day OHLCV (Open, High, Low, Close, Volume) candles for a stock over a date range. Formerly `GET /market-data/ohlcv`, which remains a supported (deprecated) alias.\n\n**Current limitations:**\n- Only `interval=1d` (end-of-day) is supported. Intraday intervals are not yet available.\n- `open`, `high`, and `low` fields are `null` until intraday data is ingested — only `close` and `volume` are populated.\n- Maximum date range per request: 365 days. Split larger requests.\n\nCandles are sorted oldest-first and filtered to the `[from, to]` window inclusive.\n",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "description": "Exchange-qualified ticker (e.g. `SCOM.KE`) or bare ticker.",
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "required": true,
            "description": "Exchange code (NSE, NGX, JSE, GSE, BRVM, ZSE, BSE, LUSE, EGX, DSE, USE, MSE, CSE, SEM).",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "timeframe",
            "in": "query",
            "description": "Candle timeframe. `1D` (default, end-of-day, always available) or `session` (coarse intraday — open/mid/close samples captured 3×/day; accumulates from when capture was enabled, see `meta.coverageStartsAt`). Finer intervals (1Min/5Min/15Min/1H) are not available on the delayed feed. The legacy `interval=1d` param remains a supported alias for `1D`.\n",
            "schema": {
              "type": "string",
              "enum": [
                "1D",
                "session"
              ],
              "default": "1D"
            }
          },
          {
            "name": "interval",
            "in": "query",
            "description": "Legacy alias for `timeframe`. `1d` maps to `timeframe=1D`. Prefer `timeframe`.",
            "schema": {
              "type": "string",
              "enum": [
                "1d"
              ],
              "default": "1d"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Start date inclusive (YYYY-MM-DD). Defaults to 30 days ago.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-01-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "End date inclusive (YYYY-MM-DD). Defaults to today.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-06-04"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl \"https://mystocks.africa/api/v1/partner/market/ohlcv?symbol=SCOM.KE&exchange=NSE&from=2026-01-01&to=2026-06-04\" \\\n  -H \"x-api-key: pk_live_KEY\"\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch(\n  'https://mystocks.africa/api/v1/partner/market/ohlcv?symbol=SCOM.KE&exchange=NSE&from=2026-01-01&to=2026-06-04',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { data, meta } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.get('https://mystocks.africa/api/v1/partner/market/ohlcv',\n  params={'symbol': 'SCOM.KE', 'exchange': 'NSE', 'from': '2026-01-01', 'to': '2026-06-04'},\n  headers={'x-api-key': 'pk_live_KEY'})\ncandles = r.json()['data']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "OHLCV candles for the requested symbol and date range.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "description": "Candles sorted oldest-first. One entry per trading day with available price data.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "timestamp": {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 start-of-day UTC timestamp for the candle period.",
                            "example": "2026-01-02T00:00:00.000Z"
                          },
                          "open": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Session open price (local currency). null until intraday data is available."
                          },
                          "high": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Session high price (local currency). null until intraday data is available."
                          },
                          "low": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Session low price (local currency). null until intraday data is available."
                          },
                          "close": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "End-of-day closing price in local currency.",
                            "example": 16.5
                          },
                          "volume": {
                            "type": "integer",
                            "description": "Trading volume for the session. 0 if not recorded.",
                            "example": 4200000
                          }
                        }
                      }
                    },
                    "meta": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Meta"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "timeframe": {
                              "type": "string",
                              "description": "The resolved timeframe (`1D` or `session`).",
                              "example": "1D"
                            },
                            "interval": {
                              "type": "string",
                              "description": "Legacy alias field. `1d` for `1D`, else the timeframe.",
                              "example": "1d"
                            },
                            "coverageStartsAt": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Session timeframe only — earliest date intraday samples exist for this symbol. Absent for `1D`.\n",
                              "example": "2026-07-15"
                            },
                            "from": {
                              "type": "string",
                              "format": "date",
                              "example": "2026-01-01"
                            },
                            "to": {
                              "type": "string",
                              "format": "date",
                              "example": "2026-06-04"
                            },
                            "count": {
                              "type": "integer",
                              "description": "Number of candles returned.",
                              "example": 107
                            },
                            "ohlc_available": {
                              "type": "boolean",
                              "description": "false until intraday OHLC data is ingested. When false, open/high/low are null.",
                              "example": false
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (missing params, invalid date format, range > 365 days).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Symbol not found on the specified exchange.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMarketOhlcv"
      }
    },
    "/market-data/ohlcv": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "OHLCV price history (deprecated alias)",
        "deprecated": true,
        "description": "Deprecated alias of `GET /market/ohlcv` — same handler, parameters, and response shape. Existing integrations keep working; use `/market/ohlcv` in new code.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "SCOM.KE"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "interval",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1d"
              ],
              "default": "1d"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "See `GET /market/ohlcv`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "operationId": "getOhlcv"
      }
    },
    "/apply": {
      "post": {
        "x-sandbox": false,
        "tags": [
          "Registration"
        ],
        "summary": "Submit partner application",
        "description": "Submits a B2B partner application. No authentication is required, but the caller must accept the current Partner Terms and Privacy Notice. The response is deliberately identical for new and previously known email addresses. No credential or credential fingerprint is returned. Create or sign in to a verified partner account to retrieve sandbox credentials through the authenticated registration flow.\n",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl -X POST https://mystocks.africa/api/v1/partner/apply \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"businessName\":\"Savanna Capital Partners\",\"email\":\"engineering@savannacapital.example\",\"useCase\":\"Embed reviewed stock-order workflows\",\"website\":\"https://savannacapital.example\",\"termsAccepted\":true,\"consentVersion\":\"terms:2026-07-30|privacy:2026-07-30\"}'\n"
          },
          {
            "lang": "JavaScript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://mystocks.africa/api/v1/partner/apply', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    businessName: 'Savanna Capital Partners',\n    email: 'engineering@savannacapital.example',\n    useCase: 'Embed reviewed stock-order workflows',\n    website: 'https://savannacapital.example',\n    termsAccepted: true,\n    consentVersion: 'terms:2026-07-30|privacy:2026-07-30',\n  }),\n});\nconst { message } = await res.json();\n"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import requests\nr = requests.post('https://mystocks.africa/api/v1/partner/apply',\n  json={\n    'businessName': 'Savanna Capital Partners',\n    'email': 'engineering@savannacapital.example',\n    'useCase': 'Embed reviewed stock-order workflows',\n    'termsAccepted': True,\n    'consentVersion': 'terms:2026-07-30|privacy:2026-07-30',\n  })\nmessage = r.json()['message']\n"
          }
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "businessName",
                  "email",
                  "useCase",
                  "termsAccepted",
                  "consentVersion"
                ],
                "properties": {
                  "businessName": {
                    "type": "string",
                    "minLength": 2,
                    "description": "Your company or project name.",
                    "example": "Savanna Capital Partners"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Business contact email. Used to prevent duplicate applications.",
                    "example": "engineering@savannacapital.example"
                  },
                  "website": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional company website URL.",
                    "example": "https://savannacapital.example"
                  },
                  "useCase": {
                    "type": "string",
                    "description": "Brief description of your integration use case.",
                    "example": "Embed stock trading into our neobank app"
                  },
                  "description": {
                    "type": "string",
                    "description": "Optional additional context about your product or team."
                  },
                  "termsAccepted": {
                    "type": "boolean",
                    "enum": [
                      true
                    ],
                    "description": "Confirms acceptance of the current Partner Terms and acknowledgement of the Privacy Notice."
                  },
                  "consentVersion": {
                    "type": "string",
                    "enum": [
                      "terms:2026-07-30|privacy:2026-07-30"
                    ],
                    "description": "Version identifier displayed by the application client."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Application accepted for processing. The response never reveals account existence or credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Application received. Check your email for the next step."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid fields (businessName, email, or useCase).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "submitPartnerApplication"
      }
    },
    "/corporate-actions": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "List corporate actions across symbols and exchanges",
        "description": "Returns authoritative mandatory and voluntary corporate actions. Filter by symbol, exchange, type, or lifecycle status.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Corporate-action registry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "actions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CorporateAction"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listCorporateActions"
      }
    },
    "/corporate-actions/{actionId}": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Get authoritative corporate-action details",
        "parameters": [
          {
            "name": "actionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Corporate action, options, deadlines, default treatment, terms, and source documents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "action": {
                      "$ref": "#/components/schemas/CorporateAction"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Corporate action not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getCorporateAction"
      }
    },
    "/users/{userId}/corporate-actions/{actionId}/entitlement": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Get record-date corporate-action entitlement",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Eligibility, eligible units, election, and custodian evidence.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "corporateActionId": {
                      "type": "string"
                    },
                    "eligible": {
                      "type": "boolean"
                    },
                    "entitlement": {
                      "type": [
                        "object",
                        "null"
                      ]
                    },
                    "election": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/GovernanceInstruction"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getCorporateActionEntitlement"
      }
    },
    "/users/{userId}/corporate-actions/{actionId}/elections": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Get election and custodian-acceptance status",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Election status and confirmation evidence",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "eligible": {
                      "type": "boolean"
                    },
                    "entitlement": {
                      "type": [
                        "object",
                        "null"
                      ]
                    },
                    "election": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/GovernanceInstruction"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getCorporateActionElection"
      },
      "post": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Submit or amend a corporate-action election",
        "description": "Reusing the same sub-account and action amends the instruction while voting remains open. Every accepted revision returns to PENDING_CUSTODIAN.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "actionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "optionId",
                  "acknowledgement"
                ],
                "properties": {
                  "optionId": {
                    "type": "string"
                  },
                  "units": {
                    "type": "number",
                    "minimum": 0
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "acknowledgement": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Election revision queued for custodian acceptance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "election": {
                      "$ref": "#/components/schemas/GovernanceInstruction"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "submitCorporateActionElection"
      }
    },
    "/shareholder-meetings": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "List AGM, EGM, court, and class meetings",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exchange",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Shareholder meeting registry",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meetings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ShareholderMeeting"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listShareholderMeetings"
      }
    },
    "/shareholder-meetings/{meetingId}": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Get meeting, resolutions, proxy documents, and ballot choices",
        "parameters": [
          {
            "name": "meetingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Authoritative meeting record",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meeting": {
                      "$ref": "#/components/schemas/ShareholderMeeting"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getShareholderMeeting"
      }
    },
    "/users/{userId}/shareholder-meetings/{meetingId}/ballot": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Get record-date voting entitlement and ballot",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "meetingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Meeting",
            "voting units": null,
            "resolutions": null,
            "choices": null,
            "and any existing vote": null,
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meeting": {
                      "$ref": "#/components/schemas/ShareholderMeeting"
                    },
                    "eligible": {
                      "type": "boolean"
                    },
                    "entitlement": {
                      "type": [
                        "object",
                        "null"
                      ]
                    },
                    "ballot": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "vote": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/GovernanceInstruction"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getShareholderBallot"
      }
    },
    "/users/{userId}/shareholder-meetings/{meetingId}/votes": {
      "get": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Get vote and custodian-confirmation evidence",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "meetingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Vote status and confirmation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "eligible": {
                      "type": "boolean"
                    },
                    "entitlement": {
                      "type": [
                        "object",
                        "null"
                      ]
                    },
                    "vote": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/GovernanceInstruction"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getShareholderVote"
      },
      "post": {
        "tags": [
          "Corporate Governance"
        ],
        "summary": "Submit or amend resolution-level voting instructions",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "meetingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Vote revision queued for custodian acceptance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "vote": {
                      "$ref": "#/components/schemas/GovernanceInstruction"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "submitShareholderVote"
      }
    },
    "/team": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "List organization members, roles, invitations, and access policy",
        "description": "Console-member endpoint. Human RBAC is enforced independently from machine API-key scopes.",
        "responses": {
          "200": {
            "description": "Organization access registry",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PartnerMember"
                      }
                    },
                    "roles": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PartnerRole"
                      }
                    },
                    "invitations": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getPartnerTeam"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Update organization access policy",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "requireMfaForPrivilegedRoles": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access policy updated"
          }
        },
        "operationId": "updatePartnerAccessPolicy"
      }
    },
    "/team/invites": {
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Invite a verified-email member",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "roleIds"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "displayName": {
                    "type": "string"
                  },
                  "roleIds": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invitation created with a seven-day expiry"
          }
        },
        "operationId": "invitePartnerMember"
      },
      "delete": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Cancel a pending invitation",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invitation cancelled"
          }
        },
        "operationId": "cancelPartnerInvitation"
      }
    },
    "/team/members/{memberId}": {
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Change a member's roles or access status",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "memberId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "roleIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "SUSPENDED",
                      "REMOVED"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Member and active sessions updated"
          },
          "409": {
            "description": "Final active Owner cannot be removed"
          }
        },
        "operationId": "updatePartnerMember"
      }
    },
    "/team/roles": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "List system and custom roles and the permission catalogue",
        "responses": {
          "200": {
            "description": "Roles and permissions"
          }
        },
        "operationId": "listPartnerRoles"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Create a custom partner role",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "permissions"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "permissions": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Custom role created"
          }
        },
        "operationId": "createPartnerRole"
      }
    },
    "/sessions": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "List active and historical partner-console sessions",
        "responses": {
          "200": {
            "description": "Organization sessions"
          }
        },
        "operationId": "listPartnerSessions"
      },
      "delete": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Revoke a partner-console session",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session revoked immediately"
          }
        },
        "operationId": "revokePartnerSession"
      }
    },
    "/team/roles/{roleId}": {
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Update a custom role and recalculate assigned-member permissions",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "roleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "permissions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Role and assigned members updated"
          }
        },
        "operationId": "updatePartnerRole"
      },
      "delete": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Delete an unused custom role",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "roleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Role deleted"
          },
          "409": {
            "description": "Active members still use the role"
          }
        },
        "operationId": "deletePartnerRole"
      }
    },
    "/support": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Support"
        ],
        "summary": "List partner support cases",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Support case list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tickets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PartnerSupportTicket"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPartnerSupportTickets"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Support"
        ],
        "summary": "Open a partner support case with SLA clocks",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject",
                  "category",
                  "severity",
                  "message"
                ],
                "properties": {
                  "subject": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string"
                  },
                  "severity": {
                    "type": "string",
                    "enum": [
                      "P0",
                      "P1",
                      "P2",
                      "P3"
                    ]
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "PRODUCTION",
                      "SANDBOX"
                    ]
                  },
                  "product": {
                    "type": "string"
                  },
                  "requestId": {
                    "type": "string"
                  },
                  "orderId": {
                    "type": "string"
                  },
                  "subAccountId": {
                    "type": "string"
                  },
                  "transactionId": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Support case created"
          }
        },
        "operationId": "createPartnerSupportTicket"
      }
    },
    "/support/{ticketId}": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Support"
        ],
        "summary": "Get a support case and partner-visible conversation",
        "parameters": [
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Support case detail and messages"
          }
        },
        "operationId": "getPartnerSupportTicket"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Support"
        ],
        "summary": "Resolve or close a partner support case",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "RESOLVED",
                      "CLOSED"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Support case updated"
          }
        },
        "operationId": "closePartnerSupportTicket"
      }
    },
    "/support/{ticketId}/messages": {
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Support"
        ],
        "summary": "Add a partner-visible support message",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string"
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reply added and case moved to WAITING_ON_MYSTOCKS"
          }
        },
        "operationId": "replyToPartnerSupportTicket"
      }
    },
    "/approvals/policies": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Retrieve maker-checker policy",
        "responses": {
          "200": {
            "description": "Approval policy and supported actions"
          }
        },
        "operationId": "getPartnerApprovalPolicy"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Update maker-checker policy",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approval policy updated"
          }
        },
        "operationId": "updatePartnerApprovalPolicy"
      }
    },
    "/approvals": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "List organization approval requests",
        "responses": {
          "200": {
            "description": "Approval inbox"
          }
        },
        "operationId": "listPartnerApprovals"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Submit an operation for maker-checker approval",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "actionType",
                  "payload"
                ],
                "properties": {
                  "actionType": {
                    "type": "string",
                    "enum": [
                      "PAYOUT_REQUEST",
                      "FLOAT_LIMIT_CHANGE",
                      "API_KEY_CREATE",
                      "API_KEY_REVOKE",
                      "WEBHOOK_CHANGE",
                      "SECURITY_POLICY_CHANGE",
                      "ACCOUNT_FREEZE",
                      "ACCOUNT_CLOSE",
                      "BULK_CUSTOMER_OPERATION",
                      "CORPORATE_ACTION_ELECTION",
                      "PROXY_VOTE"
                    ]
                  },
                  "title": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "number"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "payload": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval request created"
          }
        },
        "operationId": "createPartnerApproval"
      }
    },
    "/approvals/{approvalId}": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Retrieve approval evidence and event history",
        "parameters": [
          {
            "name": "approvalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Approval request and immutable events"
          }
        },
        "operationId": "getPartnerApproval"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Approve, reject, cancel, or delegate a request",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "approvalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "APPROVE",
                      "REJECT",
                      "CANCEL",
                      "DELEGATE"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  },
                  "memberId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approval decision recorded"
          },
          "403": {
            "description": "Self-approval or delegation policy denied the action"
          },
          "409": {
            "description": "Request expired",
            "was already decided": null,
            "or evidence did not match": null
          }
        },
        "operationId": "decidePartnerApproval"
      }
    },
    "/enterprise/sso": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Retrieve enterprise SSO configuration",
        "responses": {
          "200": {
            "description": "SAML or OIDC configuration"
          }
        },
        "operationId": "getPartnerSso"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Configure SAML or OIDC SSO",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SSO configuration saved"
          }
        },
        "operationId": "updatePartnerSso"
      }
    },
    "/enterprise/scim": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "List SCIM provisioning tokens",
        "responses": {
          "200": {
            "description": "Masked SCIM tokens and base URL"
          }
        },
        "operationId": "listPartnerScimTokens"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Create a SCIM provisioning token",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "SCIM token shown once"
          }
        },
        "operationId": "createPartnerScimToken"
      },
      "delete": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Revoke a SCIM provisioning token",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SCIM token revoked"
          }
        },
        "operationId": "revokePartnerScimToken"
      }
    },
    "/enterprise/access-reviews": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "List access certification reviews",
        "responses": {
          "200": {
            "description": "Access reviews"
          }
        },
        "operationId": "listPartnerAccessReviews"
      },
      "post": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Start an access certification review",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "dueInDays": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Access review created"
          }
        },
        "operationId": "createPartnerAccessReview"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Configure recurring access certifications",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled",
                  "cadenceDays"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "cadenceDays": {
                    "type": "integer",
                    "enum": [
                      30,
                      90,
                      180,
                      365
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access review schedule updated"
          }
        },
        "operationId": "schedulePartnerAccessReviews"
      }
    },
    "/enterprise/access-reviews/{reviewId}": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Retrieve access review subjects",
        "parameters": [
          {
            "name": "reviewId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Access review details"
          }
        },
        "operationId": "getPartnerAccessReview"
      },
      "patch": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Record an access certification decision",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "name": "reviewId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "memberId",
                  "decision"
                ],
                "properties": {
                  "memberId": {
                    "type": "string"
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "CONFIRM",
                      "REVOKE",
                      "CHANGE_REQUIRED"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access decision recorded"
          }
        },
        "operationId": "decidePartnerAccessReview"
      }
    },
    "/enterprise/evidence": {
      "get": {
        "x-sandbox": false,
        "tags": [
          "Partner Administration"
        ],
        "summary": "Export security and compliance evidence",
        "responses": {
          "200": {
            "description": "Downloadable JSON evidence bundle with SHA-256 Digest header"
          }
        },
        "operationId": "exportPartnerEvidence"
      }
    }
  }
}
