pocoaiwebsdk
v1.1.0
Published
PocoAI Gift Card SDK for the web — embed a gift card rewards store on your website with an iframe wrapper
Readme
pocoaiwebsdk
PocoAI Gift Card SDK for the web. Embed the rewards store on your website in a few lines.
Install
npm install pocoaiwebsdkQuick start
<div id="rewards-store" style="width: 100%; height: 720px"></div>
<script type="module">
import { SubspaceSDK } from 'pocoaiwebsdk';
SubspaceSDK.init({
container: '#rewards-store',
auth: 'YOUR_AUTH_TOKEN',
clientId: 'YOUR_CLIENT_ID',
onReady: () => console.log('ready'),
onPurchase: (d) => console.log('purchase', d),
onClose: () => console.log('close'),
onError: (e) => console.warn('error', e),
});
</script>Call SubspaceSDK.destroy() to tear down the iframe and listeners.
Config
| Field | Type | Required | Description |
|---|---|---|---|
| container | string \| HTMLElement | yes | CSS selector or element to mount the iframe into |
| auth | string \| null | no | Partner-issued JWT. Omit (or pass null/undefined/"") for guest mode — see below |
| clientId | string | yes | Partner-issued client id |
| initialRoute | SDKSection | no | Pin the SDK to a single section — see below |
| productId | string | no | Deep-link to a specific item within the focused section. With initialRoute: 'giftcards' → opens /product/<id>; with initialRoute: 'subscriptions' → opens /subscriptions/<id>. Ignored otherwise. |
| theme | SDKTheme | no | Runtime theme override (merged on top of the per-client config). See Theme override. |
| onReady | () => void | no | Fires when the SDK is ready to receive postMessages |
| onPurchase | ({ orderId, amount, brand }) => void | no | Fires when a gift card purchase completes |
| onSubscriptionJoined | ({ room_id, service_name?, plan_name? }) => void | no | Fires when the user joins a shared-subscription room |
| onClose | () => void | no | Fires when the user requests to close |
| onError | ({ code, message }) => void | no | Fires on SDK errors — see Error codes |
Guest mode
auth is optional. Omit it, or pass null / undefined / "", to open the SDK without a signed-in user. Guests can browse gift cards and brands, but authenticated-only sections (orders, chats, quick-payments) render an "Authentication required" screen.
// Authenticated
SubspaceSDK.init({
container: '#rewards-store',
auth: 'YOUR_AUTH_TOKEN',
clientId: 'YOUR_CLIENT_ID',
});
// Guest
SubspaceSDK.init({
container: '#rewards-store',
clientId: 'YOUR_CLIENT_ID',
});The string literals "null" and "undefined" are also treated as guest, so auth: String(maybeToken) is safe even when maybeToken is null or undefined.
initialRoute — pin the SDK to one section
Omit initialRoute for the full rewards store (default). Set it to bind a button on your site to a single section — the user lands there with a trimmed header and stays within that section.
| Value | Opens |
|---|---|
| 'transactions' | Order history |
| 'chats' | Chat inbox |
| 'subscriptions' | Shared subscriptions |
| 'quick-payments' | Bill payments (BBPS) |
| 'giftcards' | Gift-card store (BBPS + subscription tiles auto-hidden) |
SubspaceSDK.init({
container: '#my-transactions',
auth: TOKEN,
clientId: CID,
initialRoute: 'transactions',
});productId — deep-link to a specific item
Pair initialRoute with productId to open the SDK directly on a specific gift-card brand or subscription plan, skipping the section's hub list:
// Open straight on the Amazon gift-card page
SubspaceSDK.init({
container: '#rewards-store',
auth: TOKEN,
clientId: CID,
initialRoute: 'giftcards',
productId: 'c098ee4a-b886-47c5-8b6f-093f30211163', // brand UUID
});
// Open straight on the Netflix Premium subscription plan
SubspaceSDK.init({
container: '#subs-button',
auth: TOKEN,
clientId: CID,
initialRoute: 'subscriptions',
productId: 'b2e557ab-4154-4f5a-8a95-018c92a0d779', // plan UUID
});To find the right productId: open the SDK in a browser, navigate to the brand or plan, and copy the UUID from the URL (https://sdk.pocoai.store/product/<id> for gift cards, /subscriptions/<id> for plans).
productId is ignored for transactions, chats, and quick-payments.
Browser permissions
The SDK's delivery-address picker uses navigator.geolocation. The iframe is created with allow="clipboard-write; geolocation" automatically — no partner code change needed in the common case.
If your site sets a strict Permissions-Policy header, you must relax the geolocation directive so the SDK's origin is allowed. Example:
Permissions-Policy: geolocation=(self "https://sdk.pocoai.store")Partners who omit that header entirely are unaffected. Users can always type or search for an address manually if they deny the browser prompt — "Detect my location" is the only feature that needs the permission.
Framework integration
init() touches document and window, so call it after your component mounts (not at module top-level). Importing the package is SSR-safe — only init() is browser-only.
React / Next.js
In Next.js App Router, the file must be a client component ('use client').
'use client';
import { useEffect, useRef } from 'react';
import { SubspaceSDK } from 'pocoaiwebsdk';
export function RewardsStore() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!ref.current) return;
SubspaceSDK.init({
container: ref.current,
auth: process.env.NEXT_PUBLIC_AUTH_TOKEN,
clientId: process.env.NEXT_PUBLIC_CLIENT_ID!,
onPurchase: (p) => console.log('purchase', p),
});
return () => SubspaceSDK.destroy();
}, []);
return <div ref={ref} style={{ width: '100%', height: 720 }} />;
}The same pattern works in Vite/CRA React — drop the 'use client' directive.
Vue (Nuxt 3)
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { SubspaceSDK } from 'pocoaiwebsdk';
const container = ref<HTMLElement | null>(null);
onMounted(() => {
if (!container.value) return;
SubspaceSDK.init({
container: container.value,
auth: AUTH_TOKEN,
clientId: CLIENT_ID,
});
});
onBeforeUnmount(() => SubspaceSDK.destroy());
</script>
<template>
<div ref="container" style="width: 100%; height: 720px" />
</template>In Nuxt with SSR enabled, wrap the component in <ClientOnly> at the call site.
SvelteKit
<script lang="ts">
import { onMount } from 'svelte';
import { SubspaceSDK } from 'pocoaiwebsdk';
let container: HTMLDivElement;
onMount(() => {
SubspaceSDK.init({ container, auth: AUTH, clientId: CID });
return () => SubspaceSDK.destroy();
});
</script>
<div bind:this={container} style="width: 100%; height: 720px"></div>Theme override
Theme is normally configured per client at provisioning time, so most embeds work without passing theme at all. Pass it only when you need to override the per-client defaults at runtime (dark-mode toggles, multi-tenant white-labelling, etc.). Whatever you pass is merged on top of the per-client config, so you only need to specify the keys you want to change.
| Key | Type | Notes |
|---|---|---|
| primaryColor | string | Buttons, active states |
| accentColor | string | Discounts, savings highlights |
| backgroundColor | string | Page background |
| surfaceColor | string | Card backgrounds |
| textColor | string | Primary text |
| textSecondaryColor | string | Secondary text |
| fontFamily | string | CSS font-family value |
| borderRadius | 'sharp' \| 'rounded' \| 'pill' | Corner radius scale |
| mode | 'light' \| 'dark' | Color mode |
| logoUrl | string | Header logo image URL |
| storeTitle | string | Header title text |
| ctaText | string | Primary CTA copy |
| hideSections | string[] | Hide header surfaces — e.g. ['chat', 'orders', 'location'] |
SubspaceSDK.init({
container: '#rewards-store',
auth: TOKEN,
clientId: CID,
theme: { mode: 'dark', primaryColor: '#FF6B00' },
});Error codes
onError payload codes emitted by this wrapper or by the SDK:
| Code | Cause |
|---|---|
| INVALID_CLIENT_ID | clientId was empty or missing on init(). |
| AUTH_REQUIRED | The provided JWT expired or was revoked mid-session. Re-issue a fresh token and call init() again. |
TypeScript
Exported types: SubspaceSDKConfig, SDKSection, SDKTheme, PurchasePayload, SubscriptionJoinedPayload, ErrorPayload. Import from pocoaiwebsdk directly.
API
SubspaceSDK.init(config)— mount the iframe.SubspaceSDK.destroy()— remove the iframe and clean up listeners.SubspaceSDK.navigate(page)— advanced runtime navigation. Most partners useinitialRouteinstead and never call this.
Troubleshooting
| Symptom | Diagnosis |
|---|---|
| Build fails with document is not defined (Next.js / Nuxt / SvelteKit) | init() was called at module top-level. Move it inside useEffect / onMounted / onMount — see Framework integration. |
| [SubspaceSDK] Container not found in console | The CSS selector resolves to nothing at the time init() runs. Pass an HTMLElement (via ref) instead of a selector — guarantees the element exists. |
| Iframe is blank / shows a loading spinner forever | Most often an expired or malformed JWT. Decode it at jwt.io and check exp. Also confirm clientId matches a real partner row. |
| "Detect my location" silently fails on a strict site | The site has a Permissions-Policy header that blocks geolocation. Relax it — see Browser permissions. |
| UPI button does nothing on desktop | Expected. UPI is mobile-only; desktop browsers won't resolve upi:// schemes. The flow works on mobile devices with a UPI app installed. |
| Theme override not applying after init() | The wrapper applies theme once, on SUBSPACE_READY. To change theme after that, call destroy() then init() again with the new theme. |
| AUTH_REQUIRED fires partway through a session | The JWT expired or was revoked. Re-issue a fresh token and call init() again with the new value (init auto-destroys the previous instance). |
