Developers

One package for login and messages

Use @sneekin/auth to authenticate users or send OTP and generic messages over SMS, WhatsApp, and email. Keep one API key on your server.

Sneek's own sign-in is being rebuilt as a certified OpenID Provider. The verification endpoints below (/api/auth/request, /api/auth/verify) and the current @sneekin/auth/@sneekin/ui client contract shown here are temporarily unavailable for new integrations while partner sign-in is re-platformed onto real OIDC. Messaging (/api/messages/send) is unaffected.

1. Get an API key

Create an organization and application in the Sneek console, then open Applications → API keys. Keys are shown once. snk_test_… keys hit dev adapters (no real delivery); snk_live_… keys deliver for real. 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.

bash
curl -X POST https://api.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:

bash
curl -X POST https://api.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 package

bash
npm install @sneekin/auth
ts
import { Sneek } from '@sneekin/auth';

const sneek = new Sneek({ apiKey: process.env.SNEEK_API_KEY! });

const request = await sneek.auth.requestVerification({ identifier });
const session = await sneek.auth.verify({
    requestId: request.requestId,
    code,
});

// OTP intent -> uses the approved auth template
await sneek.messages.send({
  to: '+919876543210',
  channel: 'auto',
  type: 'otp',
  body: 'Your code is 482917',
});

// Communication intent -> uses the utility template
await sneek.messages.send({
  to: '+919876543210',
  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 renders the whole passwordless flow — enter email/mobile → enter code → signed in. You never name a channel: Sneek picks one per application and the card reports what it chose.

bash
npm install @sneekin/ui
tsx
import { SneekLogin, createFetchHandlers } from '@sneekin/ui';

export function LoginPage() {
  return (
    <SneekLogin
      title="Sign in to ConfHub"
      {...createFetchHandlers({
        requestUrl: '/api/auth/request',
        verifyUrl: '/api/auth/verify',
      })}
      onSuccess={() => (window.location.href = '/dashboard')}
    />
  );
}
Keep your key server-side. The browser component never holds a Sneek API key. It calls your backend endpoints, which call Sneek with the secret key:
ts
// POST /api/auth/request  (your server, secret key stays here)
import { Sneek } from '@sneekin/auth';
const sneek = new Sneek({ apiUrl, apiKey: process.env.SNEEK_API_KEY! });

// You ask for a verification. Sneek picks the channel from the
// application's authChannels bitmask and reports it back for display.
const request = await sneek.auth.requestVerification({ identifier });
return {
  requestId: request.requestId,
  channel: request.channel,
  maskedIdentifier: request.maskedIdentifier,
  expiresAt: request.expiresAt,
};

Errors

Non-2xx responses return RFC-7807 problem details:

json
{
  "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.
bash
curl -X POST https://api.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" }'

The Node SDK exposes it directly and pairs it with automatic 5xx retries:

ts
await sneek.messages.send({
  to: '+919876543210',
  type: 'otp',
  body: 'Your code is 482917',
  idempotencyKey: 'order-4815-otp', // same key -> delivered once
});

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? Email care@sneek.in.