{
  "openapi": "3.1.0",
  "info": {
    "title": "MyStocks Africa Partner API",
    "version": "1",
    "description": "REST API for embedding African and global stock trading into your product. Get real-time 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\nEasily fetch real-time stock prices, quote trade fees, execute orders, create multi-currency USD 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## 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\n## Authentication\nPass your key via **either** header:\n`Authorization: Bearer sk_sandbox_KEY`  or  `x-api-key: sk_sandbox_KEY`\n\n## Idempotency\nPass `Idempotency-Key` on deposit, withdraw, and trade calls.\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 / Starter | 100 |\n| Growth | 500 |\n| Enterprise | 2,000 |\n\nEvery response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.\nOn a `429` response, `Retry-After` (seconds until reset) is also set.\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:\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 8+ African exchanges. Supports real-time and historical stock price feeds for the Nairobi Stock Exchange (NSE), Nigerian Exchange Group (NGX), Johannesburg Stock Exchange (JSE), Ghana Stock Exchange (GSE), BRVM, LuSE, USE, and DSE."
    },
    {
      "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": "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', (req, res) => {\n  const signature = req.headers['x-mystocks-signature'];\n  const payload = JSON.stringify(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  // 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": "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": "Sandbox Testing",
      "description": "Live-key (pk_live_) endpoints for end-to-end webhook integration testing."
    }
  ],
  "security": [
    {
      "BearerAuth": []
    },
    {
      "ApiKeyHeader": []
    }
  ],
  "webhooks": {
    "order.pending": {
      "post": {
        "summary": "Order submitted — pending admin review",
        "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.filled": {
      "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": {
      "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": {
      "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": {
      "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": {
      "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": {
      "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."
          }
        }
      }
    },
    "kyc.updated": {
      "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"
                        ]
                      },
                      "kycLevel": {
                        "type": "string",
                        "enum": [
                          "NONE",
                          "BASIC",
                          "FULL"
                        ]
                      },
                      "reference": {
                        "type": "string",
                        "nullable": true,
                        "description": "Partner-supplied KYC reference, if provided."
                      },
                      "overriddenByAdmin": {
                        "type": "boolean",
                        "description": "Present and true when overridden by MyStocks compliance."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook received successfully."
          }
        }
      }
    },
    "account.frozen": {
      "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": {
      "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."
          }
        }
      }
    },
    "incident.declared": {
      "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": {
      "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."
          }
        }
      }
    }
  },
  "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."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Unique string that makes money-movement calls safe to retry on network failure.\nProvide on every deposit, withdraw, and trade request.\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",
          "example": "dep_user42_1743152580"
        }
      }
    },
    "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"
              }
            }
          }
        }
      },
      "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"
          }
        }
      },
      "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."
          }
        }
      },
      "DepositRequest": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "description": "USD amount to deposit into the sub-account. Deducted from your master wallet.",
            "example": 500
          },
          "note": {
            "type": "string",
            "description": "Optional description shown in the transaction history.",
            "example": "Mpesa STK push ref KE2482"
          },
          "localAmount": {
            "type": "number",
            "description": "Equivalent amount in the user local currency. Stored for audit and compliance only.",
            "example": 65000
          },
          "localCurrency": {
            "type": "string",
            "description": "ISO 4217 code for the local amount.",
            "example": "KES"
          },
          "fxRate": {
            "type": "number",
            "description": "Exchange rate you applied (local units per USD). Stored for audit -- does not affect wallet credit.",
            "example": 130
          }
        }
      },
      "WithdrawRequest": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "description": "USD amount to move from the sub-account back to your master wallet.",
            "example": 200
          },
          "note": {
            "type": "string",
            "description": "Optional description shown in transaction history."
          },
          "localAmount": {
            "type": "number",
            "description": "Equivalent in local currency for the audit trail.",
            "example": 26000
          },
          "localCurrency": {
            "type": "string",
            "description": "ISO 4217 code.",
            "example": "KES"
          },
          "fxRate": {
            "type": "number",
            "description": "Exchange rate applied for this withdrawal.",
            "example": 130
          }
        }
      },
      "TradeRequest": {
        "type": "object",
        "required": [
          "symbol",
          "type",
          "quantity"
        ],
        "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": "integer",
            "minimum": 1,
            "description": "Number of whole shares. Must be a positive integer.",
            "example": 500
          },
          "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"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "NONE",
              "PENDING",
              "VERIFIED"
            ],
            "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"
          }
        }
      },
      "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",
        "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.filled",
                "order.rejected",
                "order.cancelled",
                "trade.settled",
                "trade.rejected",
                "deposit.confirmed",
                "withdraw.confirmed",
                "wallet.credited",
                "kyc.updated",
                "account.frozen",
                "dividend.paid",
                "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",
            "description": "USD amount to top up. Must be positive.",
            "example": 10000
          },
          "paymentReference": {
            "type": "string",
            "description": "Your wire transfer reference or payment ID. Used by admin to match the incoming wire.",
            "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",
            "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."
          }
        }
      },
      "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"
              }
            }
          }
        }
      },
      "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": "integer",
            "minimum": 1,
            "description": "Number of shares. Cheat codes: 100 = instant auto-fill, 999 = instant auto-reject.",
            "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."
          }
        }
      },
      "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..."
          }
        }
      },
      "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, LUSE, EGX, BSE, ZSE."
          },
          "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."
          },
          "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"
          }
        }
      },
      "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, LUSE, EGX, BSE, ZSE."
          },
          "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."
          },
          "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"
          },
          "type": {
            "type": "string",
            "enum": [
              "BUY",
              "SELL"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROCESSING",
              "COMPLETED",
              "FILLED",
              "REJECTED",
              "CANCELLED"
            ],
            "description": "PENDING after submission. PROCESSING during admin review. COMPLETED when settled (production). FILLED = instant sandbox settlement. REJECTED if declined. CANCELLED via API."
          },
          "quantity": {
            "type": "integer"
          },
          "priceAtOrder": {
            "type": "number",
            "description": "Stock price in local currency at submission."
          },
          "usdPriceAtOrder": {
            "type": "number",
            "description": "USD equivalent of priceAtOrder."
          },
          "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 dealing desk. Populated when status = REJECTED."
          },
          "cancelledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "settledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set when COMPLETED."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "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"
            ],
            "description": "VERIFIED required for trading and asset subscriptions."
          },
          "kycLevel": {
            "type": "string",
            "enum": [
              "NONE",
              "BASIC",
              "FULL"
            ],
            "description": "BASIC = ID verified. FULL = enhanced due diligence."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "frozen"
            ]
          },
          "walletBalance": {
            "type": "number",
            "example": 1250,
            "description": "Current USD wallet balance."
          }
        }
      },
      "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"
            ]
          }
        }
      },
      "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"
          }
        }
      }
    }
  },
  "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>`.\nObtain this token by signing in at the [partner dashboard](https://mystocks.africa/partners/login),\nthen copying it from the API settings tab. The token identifies your Firebase account and is\nused to link the sandbox 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 with the same Firebase token returns your existing 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": [
          {
            "BearerAuth": []
          }
        ],
        "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": {
                    "partnerId": {
                      "type": "string"
                    },
                    "businessName": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string",
                      "enum": [
                        "sandbox",
                        "starter",
                        "growth",
                        "enterprise"
                      ]
                    },
                    "walletBalance": {
                      "type": "number",
                      "description": "Master wallet balance in USD."
                    },
                    "apiKeyPrefix": {
                      "type": "string",
                      "example": "pk_live_a1b2"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "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`, `apiKey`, `businessName`, `email`\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",
        "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, apiKey, 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",
                        "not_found"
                      ]
                    },
                    "apiKey": {
                      "type": "string",
                      "description": "Present when status is active."
                    },
                    "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"
      }
    },
    "/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",
        "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",
        "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"
      }
    },
    "/etfs": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "List ETFs",
        "description": "Returns the full list of tradeable ETFs across all 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, LUSE, EGX, BSE, ZSE.",
            "schema": {
              "type": "string",
              "example": "JSE"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Search term matching the ETF ticker symbol or name.",
            "schema": {
              "type": "string",
              "example": "Gold"
            }
          }
        ],
        "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"
                    }
                  }
                }
              }
            }
          }
        },
        "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"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "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",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "price": {
                            "type": "number"
                          },
                          "usdPrice": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "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 real-time 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, LUSE, EGX, BSE, ZSE). 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",
            "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.",
            "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 real-time stock price data (refreshed every 15 minutes during trading hours) for key markets:\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), and DSE (Tanzania).\n\nFilter by exchange, sector, or asset type. Results are paginated — use `page` and `limit`. Excellent for populating stock search screens and building live tickers.\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Filter by exchange code. One of: NSE, NGX, JSE, GSE, BRVM, LUSE, EGX, BSE, ZSE.",
            "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": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "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/stocks?exchange=NSE&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/stocks?exchange=NSE&limit=20',\n  { headers: { 'x-api-key': 'pk_live_KEY' } }\n);\nconst { stocks, total } = 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', 'limit': 20},\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"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listStocks"
      }
    },
    "/stocks/{symbol}": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Get stock detail (NSE, NGX, JSE, etc.)",
        "description": "Returns full real-time 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": {
        "tags": [
          "Market Data"
        ],
        "summary": "Stock price history",
        "description": "Returns OHLCV candles for the given symbol.\nSupported periods: `1d`, `1w`, `1m`, `3m`, `6m`, `1y`, `3y`, `5y`, `max`.\nCandle interval is auto-selected based on period (daily for ≤3 m, weekly for ≤1 y, monthly beyond).\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}/chart": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Stock 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/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"
                      }
                    },
                    "prices": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    },
                    "priceHistory": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "price": {
                            "type": "number"
                          },
                          "usdPrice": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "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": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "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"
                            ]
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listCompanies"
      }
    },
    "/companies/tickers": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Ticker symbols",
        "description": "Returns a flat list of all ticker symbols across all exchanges. Useful for symbol autocomplete or validation.",
        "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"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "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"
                      }
                    },
                    "prices": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "volumes": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listChart"
      }
    },
    "/companies/{symbol}/news": {
      "get": {
        "tags": [
          "Market Data"
        ],
        "summary": "Company news",
        "description": "Returns recent news and press releases for the specified company.",
        "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/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 { articles } = 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": "Company news articles.",
            "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"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "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
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "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), next open timestamp, 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).",
            "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"
                    },
                    "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",
                              "null"
                            ],
                            "format": "date-time",
                            "description": "ISO timestamp of the next session open. Null when market is currently open."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown exchange code."
          }
        },
        "operationId": "getMarketStatus"
      }
    },
    "/market/settlement": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Settlement cycles and order processing window",
        "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 order-processing window — the target time between order submission and admin approval in production.\n\n**Important distinction**\n- **MyStocks processing window** — the time before a PENDING order moves to COMPLETED or REJECTED. Target: 4 business hours 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)."
          }
        ],
        "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 processing window.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "processingWindow": {
                      "type": "object",
                      "properties": {
                        "targetTurnaroundHours": {
                          "type": "integer",
                          "example": 4,
                          "description": "Target hours between order submission and admin review 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. 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"
      }
    },
    "/quote/{symbol}": {
      "get": {
        "tags": [
          "Trading"
        ],
        "summary": "Pre-trade fee quote",
        "description": "Returns a real-time fee quote for a symbol before placing a trade. Includes the current 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\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": true,
            "description": "Number of shares to quote.",
            "schema": {
              "type": "integer",
              "example": 500
            }
          }
        ],
        "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": {
                    "symbol": {
                      "type": "string",
                      "example": "SCOM.KE"
                    },
                    "name": {
                      "type": "string"
                    },
                    "exchange": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string",
                      "example": "KES"
                    },
                    "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 not found."
          }
        },
        "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"
                }
              }
            }
          },
          "402": {
            "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 { positions, totalValue } = 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": {
                  "type": "object",
                  "properties": {
                    "totalValueUsd": {
                      "type": "number"
                    },
                    "positions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": "string"
                          },
                          "quantity": {
                            "type": "integer"
                          },
                          "avgCostUsd": {
                            "type": "number"
                          },
                          "currentPriceUsd": {
                            "type": "number"
                          },
                          "marketValueUsd": {
                            "type": "number"
                          },
                          "unrealisedPnl": {
                            "type": "number"
                          },
                          "unrealisedPct": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listPortfolio"
      }
    },
    "/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",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED"
              ]
            }
          },
          {
            "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": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "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=COMPLETED&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=COMPLETED',\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': 'COMPLETED', '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"
            }
          }
        ],
        "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"
          },
          {
            "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' },\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'})\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 (already PROCESSING, COMPLETED, or REJECTED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteOrders"
      }
    },
    "/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"
                          },
                          "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"
                    },
                    "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"
                          },
                          "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"
                    },
                    "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",
        "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 \"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', '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'},\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",
        "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 \"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', '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'},\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": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "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 { users, total } = 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": {
                    "users": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SubAccount"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        },
        "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"
            }
          }
        ],
        "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 \"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', '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'},\n  json={'status': 'frozen'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated sub-account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          }
        },
        "operationId": "updateUsers"
      }
    },
    "/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**FX model** — you collect local currency from your user and convert to USD yourself.\nRecord the exchange rate in `fxRate` for audit. Only `amount` (USD) is credited.\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\":500,\"note\":\"Mpesa STK push ref KE2482\",\"localAmount\":65000,\"localCurrency\":\"KES\",\"fxRate\":130}'\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: 500, note: 'Mpesa STK push', localAmount: 65000, localCurrency: 'KES', fxRate: 130 }),\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': 500, 'note': 'Mpesa STK push', 'localAmount': 65000, 'localCurrency': 'KES', 'fxRate': 130})\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"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "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 `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\":200,\"note\":\"User requested withdrawal\",\"localAmount\":26000,\"localCurrency\":\"KES\",\"fxRate\":130}'\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: 200, note: 'User withdrawal', localAmount: 26000, localCurrency: 'KES', fxRate: 130 }),\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': 200, 'localAmount': 26000, 'localCurrency': 'KES', 'fxRate': 130})\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"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "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."
                    },
                    "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}/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"
                }
              }
            }
          },
          "402": {
            "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 { positions } = 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": "Portfolio positions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "totalValueUsd": {
                      "type": "number"
                    },
                    "positions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": "string"
                          },
                          "quantity": {
                            "type": "integer"
                          },
                          "avgCostUsd": {
                            "type": "number"
                          },
                          "currentPriceUsd": {
                            "type": "number"
                          },
                          "marketValueUsd": {
                            "type": "number"
                          },
                          "unrealisedPnl": {
                            "type": "number"
                          },
                          "unrealisedPct": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "getUserPortfolio"
      }
    },
    "/users/{userId}/orders": {
      "get": {
        "tags": [
          "Sub-Accounts"
        ],
        "summary": "Sub-account orders",
        "description": "Returns order history for a sub-account. Filter by status, symbol, or date range.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED"
              ]
            }
          },
          {
            "name": "symbol",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "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=COMPLETED\" \\\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=COMPLETED',\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': 'COMPLETED'}, 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 a PENDING order on a sub-account. Returns 422 if the order is already settled or rejected.",
        "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 -X DELETE \\\n  https://mystocks.africa/api/v1/partner/users/usr_abc123/orders/ord_abc123xyz \\\n  -H \"Authorization: Bearer pk_live_KEY\"\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' } }\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'})\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"
      }
    },
    "/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" }
                        }
                      }
                    },
                    "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\nThe `reference` field stores your internal KYC session ID for audit correlation.\n",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "usr_abc123"
            }
          }
        ],
        "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 \"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', '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'},\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"
                    },
                    "kycLevel": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createKyc"
      }
    },
    "/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"
            }
          }
        ],
        "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 \"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', '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'},\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"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "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"
            }
          }
        ],
        "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 \"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', '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'},\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 live 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 live prices.",
            "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"
            }
          }
        ],
        "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 \"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', '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'},\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)."
          }
        ],
        "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"
          },
          {
            "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' } }\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'})\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"
      }
    },
    "/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",
        "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 \"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', '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'},\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",
        "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 \"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', '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'},\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"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "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** — deliveries are retried with exponential back-off (3 attempts, up to 10 minutes).\nYour endpoint must respond `2xx` within 8 seconds or the delivery is marked failed.\n",
        "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 \"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', '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'},\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"
            }
          }
        ],
        "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"
          },
          {
            "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' },\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'})\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 synthetic `ping` event to the registered webhook URL. Use this to verify connectivity and HMAC signature verification before going live.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "wh_abc123"
            }
          }
        ],
        "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"
          },
          {
            "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' },\n});\nconst { delivered, statusCode } = 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'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Test delivery result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivered": {
                      "type": "boolean"
                    },
                    "statusCode": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "HTTP status your endpoint returned."
                    },
                    "latencyMs": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "error": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Error message if delivery failed."
                    }
                  }
                }
              }
            }
          }
        },
        "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/calendar": {
      "get": {
        "tags": [
          "Dividends"
        ],
        "summary": "Dividend calendar",
        "description": "Returns upcoming and recent dividend events across all exchanges — ex-date, record date, pay date, and dividend per share.",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "description": "Filter by exchange.",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "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/dividends/calendar?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/dividends/calendar?exchange=NSE',\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/dividends/calendar',\n  params={'exchange': 'NSE'}, headers={'x-api-key': 'pk_live_KEY'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Dividend calendar events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "symbol": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "exchange": {
                            "type": "string"
                          },
                          "exDate": {
                            "type": "string",
                            "format": "date"
                          },
                          "recordDate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          },
                          "payDate": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          },
                          "dividendPerShare": {
                            "type": "number"
                          },
                          "currency": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "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.",
        "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 dk_. null if not yet issued."
                    },
                    "issuedAt": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No data key issued yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "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.",
        "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"
          },
          {
            "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' },\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'})\ndata_key = r.json()['dataKey']\n"
          }
        ],
        "responses": {
          "201": {
            "description": "Data key issued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dataKey": {
                      "type": "string",
                      "example": "dk_a1b2c3d4e5f6"
                    },
                    "issuedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        },
        "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.",
        "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"
          },
          {
            "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' },\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'})\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Data key revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Data API key revoked."
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "deleteDataKey"
      }
    },
    "/api-keys/rotate": {
      "post": {
        "tags": [
          "Key Management"
        ],
        "summary": "Rotate API key",
        "description": "Issues a new primary API key and **immediately invalidates** the current one.\nThe new key is returned in the response — store it securely.\nThere is a 60-second grace period during rotation where both keys are valid to allow a zero-downtime swap.\n",
        "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"
          },
          {
            "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' },\n});\nconst { newKey } = 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'})\nnew_key = r.json()['newKey']\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Key rotated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "newKey": {
                      "type": "string",
                      "description": "New primary API key. Store immediately — shown once."
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Old key expires at this time."
                    }
                  }
                }
              }
            }
          }
        },
        "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.",
        "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 pk_live_KEY\" \\\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 pk_live_KEY', '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 pk_live_KEY'},\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."
                    }
                  }
                }
              }
            }
          },
          "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."
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "listSla"
      }
    },
    "/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.",
        "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 \"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', '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'},\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 custom SMTP configuration. Use this to verify that your email settings are correct before going live.",
        "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 \"Content-Type: application/json\" \\\n  -d '{\"action\":\"test-smtp\",\"recipient\":\"dev@acme.com\"}'\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', 'Content-Type': 'application/json' },\n  body: JSON.stringify({ action: 'test-smtp', recipient: 'dev@acme.com' }),\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'},\n  json={'action': 'test-smtp', 'recipient': 'dev@acme.com'})\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action",
                  "recipient"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "test-smtp"
                    ]
                  },
                  "recipient": {
                    "type": "string",
                    "format": "email",
                    "description": "Address to send the test email to."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "SMTP test result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivered": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "createSettings"
      }
    },
    "/sandbox/deposit": {
      "post": {
        "tags": [
          "Sandbox Testing"
        ],
        "summary": "Credit virtual funds",
        "description": "**Live key only** (`pk_live_`). Credits virtual USD to a sandbox sub-account for end-to-end webhook integration testing.\nNo real money moves. Maximum $1,000,000 per call.\nTriggers a `deposit.confirmed` webhook if one is registered.\n",
        "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/v1/partner/sandbox/deposit \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\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/v1/partner/sandbox/deposit', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', '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/v1/partner/sandbox/deposit',\n  headers={'Authorization': 'Bearer pk_live_KEY'},\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": "createSandboxDeposit"
      }
    },
    "/sandbox/trade": {
      "post": {
        "tags": [
          "Sandbox Testing"
        ],
        "summary": "Submit test trade",
        "description": "**Live key only** (`pk_live_`). Places a test trade order without hitting a real exchange.\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",
        "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/v1/partner/sandbox/trade \\\n  -H \"Authorization: Bearer pk_live_KEY\" \\\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/v1/partner/sandbox/trade', {\n  method: 'POST',\n  headers: { Authorization: 'Bearer pk_live_KEY', '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/v1/partner/sandbox/trade',\n  headers={'Authorization': 'Bearer pk_live_KEY'},\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": "createSandboxTrade"
      }
    },
    "/sandbox/orders": {
      "get": {
        "tags": [
          "Sandbox Testing"
        ],
        "summary": "List test orders",
        "description": "Returns all test orders created via the sandbox trade endpoint.",
        "parameters": [
          {
            "name": "subAccountId",
            "in": "query",
            "description": "Filter by sub-account ID.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "PENDING",
                "PROCESSING",
                "COMPLETED",
                "FILLED",
                "REJECTED",
                "CANCELLED"
              ]
            }
          },
          {
            "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/sandbox/orders?status=COMPLETED\" \\\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/sandbox/orders?status=COMPLETED',\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/sandbox/orders',\n  params={'status': 'COMPLETED'}, headers={'x-api-key': 'pk_live_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": "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. Also available at `GET /market-data/movers` (identical handler).\n",
        "parameters": [
          {
            "name": "exchange",
            "in": "query",
            "required": true,
            "description": "Exchange code (NSE, NGX, JSE, GSE, BRVM, LUSE, EGX, BSE, ZSE).",
            "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.",
            "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"
                              ]
                            },
                            "total_count": {
                              "type": "integer",
                              "description": "Total number of stocks with price change data on this exchange."
                            },
                            "page": {
                              "type": "integer"
                            },
                            "per_page": {
                              "type": "integer"
                            },
                            "has_next": {
                              "type": "boolean"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "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 (alias)",
        "description": "Alias for `GET /market/movers`. Identical handler and response shape. Prefer `/market/movers` as the canonical path.",
        "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 (alias)",
        "description": "Alias for `GET /market/quotes`. Identical handler and response shape. Prefer `/market/quotes` as the canonical path.",
        "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-data/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.\n",
        "x-codeSamples": [
          {
            "lang": "cURL",
            "label": "cURL",
            "source": "curl https://mystocks.africa/api/v1/partner/market-data/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-data/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-data/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.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "description": "Exchange code.",
                            "example": "NSE"
                          },
                          "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"
                              }
                            }
                          },
                          "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": "getMarketDataExchanges"
      }
    },
    "/market-data/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.\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, LUSE, EGX, BSE, ZSE).",
            "schema": {
              "type": "string",
              "example": "NSE"
            }
          },
          {
            "name": "interval",
            "in": "query",
            "description": "Candle interval. Only `1d` is currently supported.",
            "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-data/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-data/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-data/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.",
            "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": {
                            "interval": {
                              "type": "string",
                              "example": "1d"
                            },
                            "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": "getOhlcv"
      }
    },
    "/apply": {
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Submit partner application",
        "description": "Submits a new B2B partner application to the MyStocks team. No authentication required.\n\nOn success, a sandbox API key (`sk_sandbox_…`) is automatically provisioned so you can start building immediately — it is returned in the response. Your live production key (`pk_live_…`) is issued after the team reviews your application (typically 1–2 business days).\n\nCalling again with the same email returns an idempotent message if an application is already pending or approved.\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\":\"Acme Fintech\",\"email\":\"dev@acme.com\",\"useCase\":\"Embed stock trading into our neobank app\",\"website\":\"https://acme.com\"}'\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: 'Acme Fintech',\n    email: 'dev@acme.com',\n    useCase: 'Embed stock trading into our neobank app',\n    website: 'https://acme.com',\n  }),\n});\nconst { sandboxKey } = 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': 'Acme Fintech',\n    'email': 'dev@acme.com',\n    'useCase': 'Embed stock trading into our neobank app',\n  })\nsandbox_key = r.json().get('sandboxKey')\n"
          }
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "businessName",
                  "email",
                  "useCase"
                ],
                "properties": {
                  "businessName": {
                    "type": "string",
                    "minLength": 2,
                    "description": "Your company or project name.",
                    "example": "Acme Fintech"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Business contact email. Used to prevent duplicate applications.",
                    "example": "dev@acme.com"
                  },
                  "website": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional company website URL.",
                    "example": "https://acme.com"
                  },
                  "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."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Application received. Sandbox key provisioned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Application received. Our team will review it and contact you within 1–2 business days."
                    },
                    "sandboxKey": {
                      "type": "string",
                      "description": "Pre-provisioned sandbox API key. Use it immediately at the sandbox base URL.",
                      "example": "sk_sandbox_a1b2c3d4e5f6"
                    },
                    "note": {
                      "type": "string",
                      "description": "Instructions for using the sandbox key."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid fields (businessName, email, or useCase).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "submitPartnerApplication"
      }
    }
  }
}