Configuration
Timeouts, retries, idempotency, response unwrapping, and custom fetch.
The SDK works with only a secretKey. Everything else has a default you can override per client or per request.
import { Kyshi } from '@kyshi/mor-sdk';
const kyshi = new Kyshi({
secretKey: process.env.KYSHI_SECRET_KEY!,
environment: 'test',
timeoutMs: 30_000,
maxRetries: 2,
});Client Options
| Option | Type | Default | Notes |
|---|---|---|---|
secretKey | string | — | Required. Your sk_test_ or sk_live_ key. |
environment | test | live | test | Selects the default base URL. |
baseUrl | string | Derived from environment | Override the API host. |
timeoutMs | number | 30000 | Per-attempt timeout. |
maxRetries | number | 2 | Retries after the first attempt, so 3 attempts total. |
retryDelayMs | number | 250 | First backoff delay. |
fetch | function | Global fetch | Supply your own implementation. |
Mode is decided by your key, not by environment. A sk_live_ key moves real money regardless of what you set here — environment only chooses which host the SDK talks to. See Test Mode And Sandbox.
Request Options
Every resource method takes an optional second (or third) argument:
| Option | Notes |
|---|---|
idempotencyKey | Sent as the Idempotency-Key header. Also makes the request retryable. |
timeout via client | Per-attempt, not total. |
retries | Override maxRetries for this call. |
headers | Extra headers, merged over the defaults. |
query | Extra query parameters. |
unwrap | false returns the full envelope instead of data. |
const transfer = await kyshi.transfers.create(
{ beneficiaryId: 'bnf_abc123', amount: 50000, currency: 'NGN' },
{ idempotencyKey: 'PAYOUT-10001' },
);Retries: The Rule That Matters
The SDK retries automatically, but only where retrying is safe:
| Request | Retried? |
|---|---|
GET, DELETE | Always |
POST, PUT, PATCH with an idempotencyKey | Yes |
POST, PUT, PATCH without one | Never |
That last row is deliberate. Retrying a payout whose response was lost could send the money twice, so the SDK will not do it unless you have given it a key that makes the retry safe.
If you want a write retried, pass an idempotencyKey. If you do not, a network blip on a POST surfaces as an error and you decide what to do — usually look the record up by your own reference before trying again.
Retryable responses are 408, 409, 425, 429, 500, 502, 503, 504. Network failures retry under the same method rule.
Backoff is exponential from retryDelayMs: 250 ms, then 500 ms, then 1000 ms.
Response Unwrapping
Kyshi wraps every response in { status, message, code, data }. The SDK returns data by default, so you work with the payload directly:
const transaction = await kyshi.transactions.verify('ORDER-10001');
console.log(transaction.status); // the transaction's statusPass unwrap: false when you need the envelope itself:
const envelope = await kyshi.transactions.verify('ORDER-10001', {
unwrap: false,
});
console.log(envelope.code, envelope.message, envelope.data);Note the collision: unwrapped, .status is the transaction status (SUCCESS, PENDING). On the envelope, .status is the boolean success flag. They are different fields.
Custom Fetch
Supply your own fetch for logging, tracing, proxies, or tests:
const kyshi = new Kyshi({
secretKey: process.env.KYSHI_SECRET_KEY!,
fetch: async (url, init) => {
const started = Date.now();
const response = await fetch(url, init);
logger.info({ url: String(url), ms: Date.now() - started });
return response;
},
});The contract tests use this hook, so it is a supported extension point rather than an accident.
Reaching The Raw Client
Anything the typed resources do not cover is reachable through kyshi.client:
const result = await kyshi.client.get('some/new/endpoint', {
query: { page: 1 },
});It applies the same auth, timeout, retry, and unwrapping behaviour.
Next
- Webhooks And Errors — error classes and signature helpers
- Installation — setup and environment variables
Updated 5 days ago
