@ippis/profile-management-sdk
v1.0.1
Published
Embeddable Profile Management SDK for Node and Browser.
Readme
Embedded Profile Management SDK
TypeScript SDK for profile APIs (identity, education, experience, and related sections), optional outbound webhooks, and a React ProfileWidget you can drop into an existing app.
Features
- API key authentication (
x-api-key) for profile HTTP calls. userId-scoped helpers for identity, status, education, experience, certificates, disabilities, languages, publications, and referees.- Entry points for Node, Browser, and the React widget bundle, plus a pre-built
widget.css(no Tailwind required in the host app). - Outbound webhook POST helper and inbound signature verification.
Install
npm install @ippis/profile-management-sdkThe profile widget is a React component. Your app should already use React 18+ (and typically react-dom for mounting).
You do not need Tailwind in your application. The package ships a pre-built stylesheet that only applies inside the widget (scoped under .ippis-profile-widget-root). It does not include Tailwind Preflight, so it will not reset body, headings, or other global styles in your host app.
Integrating the ProfileWidget (React)
1. Import the component
Either import from the package root (re-exported) or from the widget subpath:
import { ProfileWidget } from "@ippis/profile-management-sdk";
// or:
import { ProfileWidget } from "@ippis/profile-management-sdk/widget";ProfileWidgetHeader is also available from the same modules if you need the header-only layout.
Import the stylesheet once in your app entry (or layout), before rendering the widget:
import "@ippis/profile-management-sdk/widget.css";2. Provide SDK config and userId
ProfileWidget loads data with createProfileSdk-compatible settings passed as sdkConfig. You must resolve userId in your app (for example via your own “resolve identity” API) before rendering.
import { ProfileWidget, type ProfileSdkConfig } from "@ippis/profile-management-sdk";
import "@ippis/profile-management-sdk/widget.css";
import { useMemo } from "react";
const sdkConfig: ProfileSdkConfig = useMemo(
() => ({
baseUrl: "/internal/profile-api", // or full URL; often proxied in dev
apiKey: process.env.NEXT_PUBLIC_PROFILE_API_KEY!, // never expose private keys in public clients without a proxy
timeoutMs: 15000,
retry: { maxRetries: 2, backoffMs: 400 },
webhook: {
url: "/internal/profile-events",
secret: process.env.PROFILE_WEBHOOK_SECRET, // prefer server-side webhooks in production
},
styles: {
theme: "light",
widthClass: "w-full max-w-4xl",
containerRadius: "rounded",
containerBgClass: "bg-slate-50",
cardBgClass: "bg-white/90",
textPrimaryClass: "text-slate-900",
textSecondaryClass: "text-slate-700",
textMutedClass: "text-slate-500",
},
}),
[],
);
export function ProfilePage({ userId }: { userId: string }) {
return (
<ProfileWidget
userId={userId}
sdkConfig={sdkConfig}
profileImage={null}
positionName="Chief Digital Officer"
unitName="MININVEST"
residenceDistrict="Kicukiro"
residenceSector="Kanombe"
residenceCell="Karama"
residenceVillage="Cyurusagara"
onEditIdentityField={(field) => {
/* open your editor for ID, phone, or residence fields */
}}
onEditEducation={(education, index) => {}}
onAddEducation={() => {}}
onEditExperience={(experience, index) => {}}
onAddExperience={() => {}}
onEditCertificate={(certificate, index) => {}}
onAddCertificate={() => {}}
onEditDisability={(disability, index) => {}}
onDeleteDisability={(disability, index) => {}}
onAddDisability={() => {}}
onEditLanguage={(language, index) => {}}
onAddLanguage={() => {}}
onEditPublication={(publication, index) => {}}
onEditReferee={(referee, index) => {}}
onAddReferee={() => {}}
/>
);
}All onEdit* / onAdd* / onDelete* handlers are optional. The UI still shows edit/add controls; without handlers you can wire them later or ignore the actions.
3. Optional: ProfileWidgetStyles and Tailwind in the host
The pre-built widget.css includes every utility the component uses by default and from the shipped source. If you pass extra Tailwind class names through sdkConfig.styles (for example a custom cardBgClass you invented), those names might not exist in widget.css yet. In that case you can:
- Stick to classes similar to the defaults in this repo’s
widget.tsx/ README examples, or - Add a small Tailwind build in your app that covers your custom tokens, or
- Open an issue / extend the package
safelistwhen forking this library.
If your entire app already uses Tailwind, you may still import widget.css (simplest, matches the published build). Avoid duplicating work by also scanning node_modules unless you know you need it.
4. Security notes
- Prefer a backend or edge proxy that adds the API key so the browser never holds a privileged secret.
- Keep webhook secrets on the server when possible; browser
webhook.secretis only appropriate for tightly controlled environments.
Quick start (Node)
import { createProfileSdk } from "@ippis/profile-management-sdk/node";
const sdk = createProfileSdk({
baseUrl: "https://api.example.com/profile",
apiKey: process.env.PROFILE_API_KEY ?? "",
webhook: {
url: "https://receiver.example.com/hooks/profile",
secret: process.env.PROFILE_WEBHOOK_SECRET,
},
});
const userId = "2207169020";
const identity = await sdk.profile.getIdentity(userId);Use @ippis/profile-management-sdk/node when you want the Node-oriented entry re-exports; behavior matches the default SDK with the same types.
Quick start (browser / fetch)
import { createProfileSdk } from "@ippis/profile-management-sdk/browser";
const sdk = createProfileSdk({
baseUrl: "/internal/profile-api",
apiKey: "your-api-key-or-empty-if-proxy-injects",
});Vite dev server proxy (example)
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
server: {
proxy: {
"/internal/profile-api": {
target: "https://internal-api.example.com",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/internal\/profile-api/, "/profile"),
},
},
},
});Webhooks
Send (outbound)
await sdk.webhooks.send({
eventType: "profile.updated",
userId,
data: { identityType, identityId, identity, experiences },
});Verify (inbound, backend)
import { verifyWebhookSignature } from "@ippis/profile-management-sdk/node";
const verified = await verifyWebhookSignature(rawBody, signatureHeader, secret, {
toleranceSeconds: 300,
});Further reading
INTEGRATION.md— HTTP usage, section fetches, and webhook flow in more detail.
Development
npm install
npm run build
npm run test
npm run dev:demo # local widget demo (see demo/)