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

OptionTypeDefaultNotes
secretKeystring—Required. Your sk_test_ or sk_live_ key.
environmenttest | livetestSelects the default base URL.
baseUrlstringDerived from environmentOverride the API host.
timeoutMsnumber30000Per-attempt timeout.
maxRetriesnumber2Retries after the first attempt, so 3 attempts total.
retryDelayMsnumber250First backoff delay.
fetchfunctionGlobal fetchSupply 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:

OptionNotes
idempotencyKeySent as the Idempotency-Key header. Also makes the request retryable.
timeout via clientPer-attempt, not total.
retriesOverride maxRetries for this call.
headersExtra headers, merged over the defaults.
queryExtra query parameters.
unwrapfalse 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:

RequestRetried?
GET, DELETEAlways
POST, PUT, PATCH with an idempotencyKeyYes
POST, PUT, PATCH without oneNever

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 status

Pass 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


Did this page help you?