@aiginy/hyperstack
v1.0.5
Published
Drop-in OAuth onboarding for any channel — WhatsApp, Instagram, Messenger, email and more. Shadow DOM modal, zero dependencies, works with any framework.
Downloads
567
Maintainers
Readme
Hyperstack
Drop-in OAuth onboarding for any channel — WhatsApp, Instagram, Messenger, email and more. Shadow DOM modal, zero dependencies — works with React, Vue, Svelte, Next.js, or plain HTML.
Installation
npm install @aiginy/hyperstackBase URL: The SDK points to
https://api.hyperstack.inby default. No configuration needed unless you're self-hosting.
Quick Start
1. Set environment variables on your server
HYPERSTACK_CLIENT_ID=cs_abc123
HYPERSTACK_CLIENT_SECRET=sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
# HYPERSTACK_URL=https://api.hyperstack.in ← default, only set to override2. Create one backend endpoint
// Example: Express
import { exchangeToken, ExchangeError } from "@aiginy/hyperstack/server";
app.post("/api/exchange-token", async (req, res) => {
try {
const result = await exchangeToken(req.body.token);
res.json(result);
} catch (err) {
if (err instanceof ExchangeError) {
res.status(err.statusCode).json({ error: err.message });
} else {
res.status(500).json({ error: "Unknown error" });
}
}
});3. Init and connect
import Hyperstack from "@aiginy/hyperstack";
const channel = Hyperstack.init({
clientId: "cs_abc123",
integrationUid: "550e8400-e29b-41d4-a716-446655440000",
// baseUrl: "https://api.hyperstack.in" — default, omit unless self-hosting
});
// On button click:
channel.connect({
customerRef: "customer-123",
onSuccess: async ({ token }) => {
const res = await fetch("/api/exchange-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
const { connection_id, status } = await res.json();
console.log("Connected!", connection_id, status);
},
onError: ({ code, message }) => {
console.error("Error:", code, message);
},
onClose: () => {
console.log("Closed without completing");
},
});Entry Points
| Import | Environment | What it does |
|--------|-------------|--------------|
| @aiginy/hyperstack | Browser | Shadow DOM modal OAuth flow (default) |
| @aiginy/hyperstack/popup | Browser | Popup window OAuth flow (legacy) |
| @aiginy/hyperstack/server | Node.js | Token exchange with client credentials |
The server entry cannot run in the browser — it reads client_secret from process.env.
Client API
Hyperstack.init(config)
| Param | Type | Required | Description |
|-------|------|----------|-------------|
| clientId | string | ✅ | Public client_id (safe for frontend) |
| integrationUid | string | ✅ | Integration UUID — identifies the channel (WhatsApp, Instagram, Messenger, email…) |
| baseUrl | string | — | Defaults to https://api.hyperstack.in. Override only if self-hosting. |
Returns a channel instance with .connect() and .close() methods.
channel.connect(options?)
| Param | Type | Description |
|-------|------|-------------|
| customerRef | string | Your internal customer identifier |
| connectionId | string | Existing connection UUID (reconnect flow) |
| brandName | string | Override brand name shown in the modal |
| brandLogo | string | Override brand logo URL shown in the modal |
| onSuccess | ({ token }) => void | Called with opaque token on completion |
| onError | ({ code, message }) => void | Called on error |
| onClose | () => void | Called when modal closed without completing |
Callback guarantee: Exactly one callback fires per connect() call.
channel.close()
Programmatically close the modal and fire onClose.
Error Codes
| Code | When |
|------|------|
| popup_blocked | Browser blocked the FB dialog popup |
| bad_fb_url | iframe returned an invalid Facebook URL |
| unknown | Unexpected error |
Server API
exchangeToken(token, config?)
Exchanges the opaque token received from onSuccess for a connection_id + status.
import { exchangeToken } from "@aiginy/hyperstack/server";
const { connection_id, status } = await exchangeToken(token);Reads credentials from env vars by default:
| Env Var | Description |
|---------|-------------|
| HYPERSTACK_CLIENT_ID | Your client_id |
| HYPERSTACK_CLIENT_SECRET | Your client_secret |
| HYPERSTACK_URL | Override the base URL (defaults to https://api.hyperstack.in) |
Or pass them explicitly:
const result = await exchangeToken(token, {
clientId: "cs_abc123",
clientSecret: "sk_live_xxx",
// serviceUrl: "https://api.hyperstack.in" — default, omit unless self-hosting
});ExchangeError
Thrown on non-2xx responses from the exchange API.
| Property | Type | Description |
|----------|------|-------------|
| statusCode | number | HTTP status (0 for network errors) |
| code | string | Machine-readable error code |
| message | string | Human-readable message |
Error codes: invalid_credentials · token_expired · bad_request · server_error · network_error · invalid_response · exchange_failed
Framework Examples
// React
const channel = Hyperstack.init({ ... });
<button onClick={() => channel.connect({ onSuccess, onError, onClose })}>
Connect Channel
</button>// Vue 3
const channel = Hyperstack.init({ ... });
channel.connect({ onSuccess, onError, onClose });<!-- SvelteKit -->
<script>
import Hyperstack from '@aiginy/hyperstack';
const channel = Hyperstack.init({ ... });
</script>
<button onclick={() => channel.connect({ onSuccess, onError, onClose })}>
Connect Channel
</button><!-- Vanilla HTML -->
<script src="https://unpkg.com/@aiginy/hyperstack/dist/client-v2.umd.js"></script>
<script>
const channel = ChannelServiceV2.init({ ... });
</script>Package Structure
@aiginy/hyperstack/
├── dist/
│ ├── client-v2.mjs ← ESM — shadow DOM modal (default)
│ ├── client-v2.cjs ← CJS — shadow DOM modal (default)
│ ├── client-v2.umd.js ← UMD — shadow DOM modal (default)
│ ├── client.mjs ← ESM — popup (legacy)
│ ├── client.cjs ← CJS — popup (legacy)
│ ├── client.umd.js ← UMD — popup (legacy)
│ ├── server.mjs ← ESM — server token exchange
│ └── server.cjs ← CJS — server token exchange
├── types/
│ ├── client-v2.d.ts ← TypeScript types (modal)
│ ├── client.d.ts ← TypeScript types (popup)
│ └── server.d.ts ← TypeScript types (server)
└── package.jsonSecurity
client_idis public — safe to ship in frontend code (like Stripe's publishable key)client_secretnever touches the browser — only used in the server helper- OAuth tokens are one-time use, deleted immediately after exchange
- All credentials encrypted at rest with AES-256-GCM
License
MIT — Secured by Hyperstack
