# SDKs and Tools

> MyStocks client source previews, multi-language examples, OpenAPI specifications, API Tester, and webhook utilities.

Use the client source previews for transport, structured errors, request IDs, timeouts, safe retries,
and webhook verification. They do not replace understanding the API contract: always read the
endpoint's authentication, scope, idempotency, and lifecycle requirements.

> `@mystocks-africa/partner-sdk` is not currently published on npm and `mystocks-partner` is not
> currently published on PyPI. Do not put either registry install command into a production build.
> Approved controlled-pilot partners receive a versioned artifact and checksum directly from
> MyStocks. Registry availability will be announced in the [changelog](/partners/docs/changelog).

Both clients now have a version-locked release pipeline. A signed `partner-sdk-vX.Y.Z` tag runs the
contract suites, builds the npm and Python distributions, verifies their metadata, produces SHA-256
checksums and build provenance, publishes through npm and PyPI trusted publishing, and attaches the
same artifacts to a GitHub release. Registry install commands will be added here only after that
release completes successfully.

## Client availability

| Language                | Package                                    | Status                                                         |
| ----------------------- | ------------------------------------------ | -------------------------------------------------------------- |
| TypeScript / JavaScript | `@mystocks-africa/partner-sdk`             | Source preview; tested and buildable, registry release pending |
| Python                  | `mystocks-partner`                         | Source preview; sync/async clients, registry release pending   |
| Raw HTTP                | `fetch`, `requests`, or any OpenAPI client | Public and recommended until the SDK registry releases         |

## Initialize a client

The SDK examples below are for approved pilot artifacts. Everyone else can use the equivalent raw
HTTP tab today.

```javascript
const BASE = "https://mystocks.africa/api/sandbox/v1/partner";
const apiKey = process.env.MYSTOCKS_API_KEY;

async function api(path, init = {}) {
return fetch(BASE + path, {
  ...init,
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", ...init.headers },
}).then(async (res) => {
  const body = await res.json();
  if (!res.ok) throw Object.assign(new Error(body.error?.message), body.error);
  return body;
});
}
```

```python
import os
import requests

BASE = "https://mystocks.africa/api/sandbox/v1/partner"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['MYSTOCKS_API_KEY']}"
```

```sdk
import { MyStocksClient } from "@mystocks-africa/partner-sdk";

const client = new MyStocksClient({
apiKey: process.env.MYSTOCKS_API_KEY!,
environment: "sandbox",
});
```

## Create a sub-account

[Try endpoint: createUsers](https://partners.mystocks.africa/partners/docs/api-tester?endpoint=createUsers)

```sh
curl -X POST https://mystocks.africa/api/sandbox/v1/partner/users \
-H "Authorization: Bearer $MYSTOCKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"externalId":"user_42","displayName":"Jane Doe","email":"jane@example.com"}'
```

```javascript
const user = await api("/users", {
method: "POST",
body: JSON.stringify({ externalId: "user_42", displayName: "Jane Doe", email: "jane@example.com" }),
});
```

```python
user = session.post(f"{BASE}/users", json={
  "externalId": "user_42", "displayName": "Jane Doe", "email": "jane@example.com"
}).json()
```

```sdk
const user = await client.subAccounts.create({
externalId: "user_42",
displayName: "Jane Doe",
email: "jane@example.com",
});
```

## Quote and place a trade

[Try endpoint: createUserTrade](https://partners.mystocks.africa/partners/docs/api-tester?endpoint=createUserTrade)

```javascript
const quote = await api("/quote/SCOM.KE?type=BUY&quantity=10&subAccountId=" + user.subAccountId);
const order = await api("/users/" + user.subAccountId + "/trade", {
method: "POST",
headers: { "Idempotency-Key": "trade_user42_scom_001" },
body: JSON.stringify({ symbol: "SCOM.KE", type: "BUY", quantity: 10, quoteId: quote.quoteId }),
});
```

```python
quote = session.get(f"{BASE}/quote/SCOM.KE", params={
  "type": "BUY", "quantity": 10, "subAccountId": user["subAccountId"]
}).json()
order = session.post(
  f"{BASE}/users/{user['subAccountId']}/trade",
  headers={"Idempotency-Key": "trade_user42_scom_001"},
  json={"symbol": "SCOM.KE", "type": "BUY", "quantity": 10, "quoteId": quote["quoteId"]},
).json()
```

```sdk
const quote = await client.trading.getQuote("SCOM.KE", {
type: "BUY", quantity: 10, subAccountId: user.subAccountId,
});
const order = await client.subAccounts.trade(
user.subAccountId,
{ symbol: "SCOM.KE", type: "BUY", quantity: 10, quoteId: quote.quoteId },
{ idempotencyKey: "trade_user42_scom_001" },
);
```

## Verify a webhook

Always verify the exact, unparsed request bytes before decoding JSON.

```javascript
const rawBody = req.body; // Buffer from express.raw({ type: "*/*" })
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const supplied = req.headers["x-mystocks-signature"] ?? "";
const valid = supplied.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(supplied), Buffer.from(expected));
```

```python
raw_body = request.get_data(cache=False)
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
valid = hmac.compare_digest(request.headers.get("x-mystocks-signature", ""), expected)
```

```sdk
const rawBody = new Uint8Array(req.body); // keep the original bytes
const valid = await verifyWebhookSignature(
rawBody,
req.headers["x-mystocks-signature"],
process.env.MYSTOCKS_WEBHOOK_SECRET!,
);
```

## API tools

[Interactive API Reference](/partners/docs/api-reference)

Browse the machine-readable OpenAPI contract, schemas, examples, and operation IDs.

[API Tester](/partners/docs/api-tester)

Exercise sandbox or production requests from the browser with a partner key.

[OpenAPI and Postman](/partners/docs/api-reference)

Download the OpenAPI document or import the published Postman collection to generate clients and test workflows.

[System Status](/status)

Live uptime, active incidents, and 30-day incident history. Raw JSON at `GET /api/v1/status`.

## Operational awareness

You do not need to poll the status page. Incidents are pushed to your registered webhooks as
`incident.declared` / `incident.resolved` and streamed on `GET /stream`. During planned
maintenance the API returns `503 MAINTENANCE` with a `Retry-After` header. See
[Errors](/partners/docs/errors) for handling guidance, including read-only maintenance mode.

## SDK behavior to rely on

- Reads and idempotent writes may retry transient `429`, `500`, `502`, `503`, and `504` responses.
- Non-idempotent writes are not automatically retried.
- Preserve the same `Idempotency-Key` when retrying a write.
- Capture `X-Request-ID` in logs and support tickets.
- Verify webhooks using the raw body and the `x-mystocks-signature` header.

Begin with [Authentication](/partners/docs/auth) and the [Broker API quickstart](/partners/docs/getting-started-broker).
