Developers
Server SDK for Node.js
Use @sneekin/sdk for shared server transport, key derivation, and payments. Keep the API key and derived keys on your server.
1. Get an API key
Create an organization and website in the Sneek console, then open Websites → API keys. Keys are shown once, and each website has one active key. Creating a replacement revokes the previous key. Send every request with Authorization: Bearer <key>.
2. Send a message (curl)
One endpoint, POST /api/messages/send. Set type to "otp" or "transactional" so Sneek selects the correct approved provider template.
curl -X POST https://sneek.in/api/messages/send \
-H "Authorization: Bearer $SNEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919876543210",
"channel": "auto",
"type": "otp",
"body": "Your code to login to ConfHub is 482917. Do not share it."
}'Prefer server-rendered DLT templates? Pass template + variables instead of body:
curl -X POST https://sneek.in/api/messages/send \
-H "Authorization: Bearer $SNEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919876543210",
"channel": "sms",
"type": "otp",
"template": "otp_login",
"variables": { "appName": "ConfHub", "otp": "482917" }
}'3. Install the Node SDK
npm install @sneekin/sdkimport { deriveKeys, SneekClient } from '@sneekin/sdk';
const sneek = new SneekClient({
apiUrl: 'https://sneek.in',
apiKey: process.env.SNEEK_API_KEY!,
});
const { qrSigningKey, webhookSigningKey, proofKey } = deriveKeys(
process.env.SNEEK_API_KEY!,
);
await sneek.request('POST', '/api/messages/send', {
to: '+919876543210',
channel: 'auto',
type: 'transactional',
body: 'Your booking BK-7781 is confirmed.',
});The SDK retries transient 5xx/network failures and throws SneekApiError with RFC-7807 problem details on other errors.
4. Drop-in login UI for React
@sneekin/ui provides the React login presentation. Its verification handler is being consolidated under @sneekin/sdk/verify in #820; S0 does not include that handler yet.
npm install @sneekin/uiimport { SneekVerify, createFetchHandlers } from '@sneekin/ui';
export function LoginPage() {
return (
<SneekVerify
title="Sign in to ConfHub"
{...createFetchHandlers({
requestUrl: '/api/auth/request',
verifyUrl: '/api/auth/verify',
})}
onSuccess={() => (window.location.href = '/dashboard')}
/>
);
}// The verification handler is delivered under @sneekin/sdk/verify in #820.
// S0 provides the shared transport and key derivation shown above.Errors
Non-2xx responses return RFC-7807 problem details:
{
"type": "https://sneek.in/errors/unauthorized",
"title": "Invalid API key",
"status": 401,
"detail": "The provided bearer token was rejected."
}Rate limits & idempotency
- Sends are rate-limited per application (its per-minute and per-day quota), per recipient, and per source IP. Exceeding a limit returns 429 with a retry_after (seconds) in the problem body.
- Send an Idempotency-Key header to make retries safe: the first request is processed and its result is replayed for any repeat with the same key, so a dropped connection never sends twice. A retry that arrives mid-flight gets 409.
curl -X POST https://sneek.in/api/messages/send \
-H "Authorization: Bearer $SNEEK_API_KEY" \
-H "Idempotency-Key: order-4815-otp" \
-H "Content-Type: application/json" \
-d '{ "to": "+919876543210", "type": "otp", "body": "Your code is 482917" }'Use the shared SDK transport with the API's idempotency header and automatic transient retries:
await sneek.request('POST', '/api/messages/send', {
to: '+919876543210',
type: 'otp',
body: 'Your code is 482917',
}, { 'Idempotency-Key': 'order-4815-otp' });Channels & intent
- channel: auto (email → email; mobile → WhatsApp then SMS fallback), or pin to sms / whatsapp / email.
- type: otp for auth codes, transactional for communications. Omitting it lets Sneek infer intent from the body, which can misclassify digit groups — pass it explicitly.
- Message wording is governed by Sneek's pre-approved templates; only your App Name and the code vary.
Need help? Contact us.