Skip to main content

@reloop-sdk/core

The framework-agnostic client. React, Vue and the vanilla widget all wrap it.

npm install @reloop-sdk/core

Create a client once, submit anywhere

// reloop.ts
import { createClient } from "@reloop-sdk/core";

export const reloop = createClient({
apiKey: "rl_pub_...",
endpoint: "https://reloop.example.com", // base URL; SDK appends /api/ingest
});

Options

OptionTypeDefaultDescription
apiKeystringPublishable key from the dashboard (required).
endpointstringBase URL of your server. SDK appends /api/ingest.

submit(item)

submit accepts one of five shapes. meta is an optional free-form object on every type; email is optional on every type (handy for replies or mailings); bug, question and other also accept an optional base64 screenshot:

import { reloop } from "./reloop";

// bug — subject + message, optional screenshot/email
await reloop.submit({
type: "bug",
subject: "Export button 404s",
message: "Clicking export on /reports returns a 404.",
email: "reporter@example.com", // optional
screenshot: "data:image/png;base64,iVBOR...", // optional
meta: { route: "/reports", appVersion: "1.2.0" },
});

// feedback — just a message
await reloop.submit({ type: "feedback", message: "Love the new dashboard!" });

// waitlist — an email
await reloop.submit({ type: "waitlist", email: "you@example.com" });

// question — subject + message
await reloop.submit({
type: "question",
subject: "Billing",
message: "Can I switch to annual billing mid-cycle?",
});

// other — escape hatch, every field optional
await reloop.submit({
type: "other",
message: "NPS score: 9",
meta: { campaign: "q3-survey" },
});

Handling errors

submit() resolves on success and rejects with a ReloopError on failure. Transient network errors are retried once; HTTP failures carry status and body:

import { reloop, ReloopError } from "@reloop-sdk/core";

try {
await reloop.submit({ type: "feedback", message: "Hi" });
} catch (err) {
if (err instanceof ReloopError) {
console.error(err.status, err.message); // e.g. 401 invalid_api_key
}
}

A complete example (no framework)

const form = document.querySelector("form")!;
const status = document.querySelector("#status")!;

form.addEventListener("submit", async (e) => {
e.preventDefault();
const email = (form.elements.namedItem("email") as HTMLInputElement).value;
status.textContent = "Joining…";
try {
await reloop.submit({ type: "waitlist", email });
status.textContent = "You're on the list! 🎉";
} catch (err) {
status.textContent = (err as Error).message;
}
});

Delivery semantics

Each submit() sends one HTTP POST and awaits the response. Transient network errors are retried once; HTTP responses (success or 4xx/5xx) are returned as-is — non-2xx responses reject with a ReloopError. The fetch uses keepalive: true so requests in flight when the page is closing have a chance to complete.