@link-apps/console-sdk
v0.2.0
Published
Official Node.js/TypeScript SDK for the Link Developer Platform (Console API) — send OTP codes through the Link bot.
Maintainers
Readme
@link-apps/console-sdk
Official Node.js / TypeScript SDK for the Link Developer Platform (Console API). Send OTP codes to your users through the Link bot with a couple of lines of code.
- Zero runtime dependencies (built on
node:crypto+ the globalfetch). - First-class TypeScript types, ESM and CommonJS builds.
- Request signing, automatic idempotency keys, transparent retries with backoff.
- Typed errors you can branch on.
Requires Node.js ≥ 18.
Install
npm install @link-apps/console-sdkQuickstart
Download a service-key JSON file from the Developer Console and load it — the SDK
reads both your credentials and the API endpoint (api_url) from the key:
import { ConsoleClient } from "@link-apps/console-sdk";
const client = ConsoleClient.fromKeyFile("./link-key.json");
// or: ConsoleClient.fromCredentialsJSON(jsonStringOrObject)
const res = await client.otp.send({
phone: "+12025550123",
code: "123456",
});
console.log(res.requestId, res.status);Authentication uses your key id + secret (HMAC request signing). The project is resolved from the key server-side — you never pass a project id.
Without a key file
If you provide the key pair directly, also pass baseUrl — the api_url from your
key. The SDK has no built-in default endpoint, so requests always go where your
key points:
const client = new ConsoleClient({
keyId: process.env.LINK_KEY_ID!,
secretKey: process.env.LINK_SECRET_KEY!,
baseUrl: process.env.LINK_API_URL!, // your key's api_url
});Sending OTP
await client.otp.send(
{
phone: "+12025550123",
code: "482913",
locale: "ru", // optional; auto-detected from the phone otherwise
templateId: "tpl_abc123", // optional; project default used otherwise
variables: { name: "Alex" }, // optional template variables ({{code}} is automatic)
},
{
idempotencyKey: "order-42-otp", // optional; auto-generated (UUID v4) otherwise
signal: AbortSignal.timeout(5000), // optional per-call cancellation
},
);Locale constants are exported for convenience: Locale.Russian, Locale.English,
Locale.Kazakh, Locale.Uzbek. Any ISO 639-1 string the platform supports is also
accepted; omit locale to auto-detect from the phone.
Idempotency & retries
Every request carries an idempotency key (auto-generated unless you pass one). Because
the request is idempotent, the SDK safely retries transient failures — network
errors, timeouts, 408/429/5xx, and the "request in progress" conflict — with
exponential backoff + jitter. Pass your own idempotencyKey to deduplicate across
process restarts.
Tune or disable retries:
new ConsoleClient({ keyId, secretKey, retry: { maxRetries: 4 } });
new ConsoleClient({ keyId, secretKey, retry: false }); // no retriesError handling
Every error extends LinkError. Branch with instanceof or the exported guards:
import {
ConsoleClient,
RateLimitError,
AuthenticationError,
isApiError,
} from "@link-apps/console-sdk";
try {
await client.otp.send({ phone, code });
} catch (err) {
if (err instanceof RateLimitError) {
console.warn("retry after", err.retryAfter);
} else if (err instanceof AuthenticationError) {
console.error("bad API key");
} else if (isApiError(err)) {
console.error(err.code, err.statusCode, err.requestId);
} else {
throw err; // ConnectionError / ValidationError / ConfigError
}
}| Error | When |
|---|---|
| ValidationError | Bad phone, code or idempotency key (thrown before any request) |
| ConfigError | Invalid credentials or client options |
| AuthenticationError | INVALID_API_KEY / INVALID_SIGNATURE |
| IdempotencyError | IDEMPOTENCY_KEY_CONFLICT (same key, different payload) |
| RateLimitError | TOO_MANY_REQUESTS (carries retryAfter) |
| ApiError | Any other non-2xx (statusCode, code, details, requestId) |
| ConnectionError | Network failure, timeout, or abort |
Configuration
| Option | Default | Description |
|---|---|---|
| keyId, secretKey | — | API key credentials (required) |
| baseUrl | from key | API endpoint (api_url). Required for the key-pair constructor; read from the key by fromKeyFile / fromCredentialsJSON |
| timeoutMs | 10000 | Per-request timeout |
| retry | { maxRetries: 2, initialBackoffMs: 100, maxBackoffMs: 2000 } | Retry policy, or false |
| userAgentSuffix | — | Appended to the User-Agent |
| fetch | global fetch | Custom fetch (proxy agent, instrumentation, tests) |
License
MIT
