Pushrail Docs
Open app
SDKs · Embed

Embed SDK quickstart

Mount the Pushrail destination-management portal in your own app with @pushrail/embed-react or @pushrail/embed-js.

Embed SDK quickstart

The Embed SDK is the client-side half of the embedded portal, the iframe your customers use to manage their own destinations from inside your product. Two packages ship: a React component and a framework-agnostic vanilla wrapper. Pick the one that matches your front end; both render the same portal against the same session-token API.

For the conceptual overview, see Embedding the portal.

What this is

The portal is a hosted iframe at embed.pushrail.io. The Embed SDK packages handle the iframe lifecycle: mounting, sizing, theming, posting events back to your page, and updating the session token without re-mounting. They do not call the Pushrail API directly; that happens server-side when your backend mints the session token.

Install

pnpm add @pushrail/embed-react
pnpm add @pushrail/embed-js

Or load the vanilla package via a <script> tag. It auto-attaches window.Pushrail.mount(...):

<script src="https://cdn.jsdelivr.net/npm/@pushrail/embed-js"></script>

Server-side: mint a session

Your backend mints a short-lived embed session token using a manage-scoped API key. See the embed sessions endpoint for the full schema.

// Inside your own backend route, behind your own auth.
const resp = await fetch("https://api.pushrail.io/embed/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PUSHRAIL_ADMIN_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    customerExternalId: req.user.customerId,
    permissions: ["destinations.read", "destinations.write"],
    ttlSeconds: 600,
  }),
});
const { token } = await resp.json();
res.json({ token });

Mint a new token per portal page load. Tokens expire automatically; do not cache them across sessions.

Restrict which destination types the session may create

Pass allowedTypes in the session request to limit which destination types your customer can create. The portal type picker shows only the listed types. When the list contains exactly one type, the type-picker step is skipped and the portal goes directly to configuration.

body: JSON.stringify({
  customerExternalId: req.user.customerId,
  permissions: ["destinations.read", "destinations.write", "routing.read", "routing.write"],
  ttlSeconds: 600,
  allowedTypes: ["WEBHOOK"],  // only webhook destinations may be created
})

The restriction is enforced server-side; a client that bypasses the UI cannot create a disallowed type.

Client-side: mount

React

import { PushrailPortal } from "@pushrail/embed-react";

export function DestinationsPage({ sessionToken }: { sessionToken: string }) {
  return (
    <PushrailPortal
      session={sessionToken}
      theme={{ colors: { primary: "#f97316" }, mode: "dark" }}
      onEvent={(ev) => {
        if (ev.type === "ready") console.log("portal mounted");
        if (ev.type === "error") console.error(ev.message);
      }}
      style={{ minHeight: 720 }}
    />
  );
}

The component auto-resizes the iframe to fit content unless you pass an explicit style.height. Theme tokens (primary color, background, border radius, font family, light/dark mode) propagate live. Changing them after mount posts an updateTheme message to the iframe rather than reloading it.

Vanilla JS

import { Pushrail } from "@pushrail/embed-js";

const instance = Pushrail.mount({
  container: "#pushrail-portal",
  token: sessionToken,
  theme: { colors: { primary: "#f97316" }, mode: "dark" },
  onReady: () => console.log("portal ready"),
  onError: (err) => console.error(err.code, err.message),
  onDestinationCreated: (dest) => refreshDestinationList(dest.id),
});

// Refresh the token without remounting (e.g., after silent re-auth):
instance.updateToken(newToken);

// Tear down on navigation away:
instance.unmount();

Both APIs share the same protocol under the hood. The React component is a thin wrapper around the same iframe + postMessage machinery the vanilla package exposes.

PushrailPortal key props

| Prop | Type | Description | |---|---|---| | session | string | The session token your backend minted. Required. | | header | "full" \| "none" | Controls the portal's own page header. Defaults to "full". Pass "none" to hide it when your host page already provides the page heading. | | suggestedEventTypes | string[] | Pre-populates the event-type routing toggles in the quick-add flow. The customer can adjust the selection, but at least one must remain checked. Pass the event types you expect this destination to handle so the customer does not have to type them from scratch. | | quickAddTitle | string | Heading shown inside the focused configurator panel when the session is scoped to a single destination type. Replaces the default heading with copy tailored to your product context. | | quickAddSubtitle | string | Supporting text shown below the heading in the focused configurator panel. Use it to explain what the destination will receive or why the customer is setting it up. | | section | "destinations" \| "deliveries" \| "events" | Pin the portal to a single top-level section. Suppresses the tab bar. | | chrome | "full" \| "none" | Show or hide portal-level navigation chrome. Defaults to "full". | | maxWidth | string \| number | Inner content max-width. Defaults to "1024px". | | theme | PushrailThemeTokens | Theme overrides: colors, border radius, font family, light/dark mode. |

Scoped quick-add

Combine allowedTypes on the session with suggestedEventTypes on the component for a focused, form-first setup experience. When allowedTypes has exactly one entry, the portal enters scoped mode:

  • The portal shows only the Configure and Delivery Logs tabs, reducing the chrome to the controls that matter for a single-destination setup.
  • If no destination exists yet, the configuration form opens immediately. The customer never sees a destination list or a type-picker.
  • The form provides a Save webhook button and a Send test button in one step so the customer can verify connectivity right away.
  • The suggestedEventTypes are pre-checked in the routing step that follows. Your customer can toggle them before saving, but must keep at least one selected.
// Backend
allowedTypes: ["WEBHOOK"]
// Frontend
<PushrailPortal
  session={token}
  header="none"
  suggestedEventTypes={["order.created", "order.refunded"]}
  quickAddTitle="Connect your webhook"
  quickAddSubtitle="We'll send a request to this URL whenever an order is created or refunded."
  style={{ minHeight: 600 }}
/>

quickAddTitle and quickAddSubtitle are optional. When omitted the portal uses its default heading. Pass them when your host page does not already provide context for what the customer is setting up.

React vs vanilla examples

Pick React when your app is already React; the PushrailPortal component plays nicely with Suspense, key-based remounting, and the React strict-mode double-mount. Pick vanilla when you need to embed inside a non-React surface (a Vue app, a Rails view with sprinkles of JS, a server-rendered admin page).

Both packages emit the same set of cross-frame events: ready, navigate, resize, error, destination.created, destination.updated. Wire them to your application's analytics and state management as needed.