Skip to main content
Give your customers or internal teams a real-time view of API traffic without leaving your app. The embed SDK renders a fully styled, CSS-isolated request log using Web Components (Shadow DOM) — no iFrames, no style conflicts.
The embed feature requires an ApiTraffic API token on your server to generate signed embed tokens. The client-side SDK never sees your API token.

How It Works

  1. Your backend calls the ApiTraffic API with your API token to generate a signed embed JWT.
  2. The JWT locks in the bucket, filters, and theme — the client SDK cannot tamper with these.
  3. Your frontend passes the JWT to the <apitraffic-request-log> Web Component.
  4. The component fetches data directly from ApiTraffic’s embed endpoints using the JWT.

Generate an Embed Token (Server-Side)

const axios = require('axios');

async function getEmbedToken(accountSid, apiToken) {
  const response = await axios.post(
    `https://api.apitraffic.io/v1/accounts/${accountSid}/embeds/token`,
    {
      bucketSid: 'your-bucket-sid',
      view: 'request-log',
      filters: {
        environmentSid: 'abc123',              // optional
        criteria: 'method == "GET"',           // optional — same filter syntax as the dashboard
        streamViewSid: 'sv_...'                // optional — use a saved stream view
      },
      ttl: 3600,                               // seconds (min 60, max 86400)
      theme: 'light'                           // 'light' | 'dark' | 'auto'
    },
    {
      headers: { Authorization: `Bearer ${apiToken}` }
    }
  );

  return response.data; // { token, expiresAt }
}

Token Parameters

ParameterTypeRequiredDefaultDescription
bucketSidstringYesThe bucket to scope the embed to.
viewstringNo'request-log'The embed view type.
filtersobjectNo{}Filters locked into the token (see below).
ttlnumberNo3600Token lifetime in seconds (60–86400).
themestringNo'light'Theme preference: 'light', 'dark', or 'auto'.

Filter Options

FilterTypeDescription
environmentSidstringFilter to a specific environment.
criteriastringSearch criteria string (same syntax as the API).
streamViewSidstringUse a saved stream view’s filter criteria.
Filters are server-side only. They are cryptographically locked into the JWT — the client SDK cannot modify, add, or remove filters. This prevents data leakage.

Vanilla JavaScript

Installation

npm install @apitraffic/embed

Basic Usage

<!-- CDN approach -->
<script src="https://unpkg.com/@apitraffic/embed/dist/apitraffic-embed.iife.js"></script>

<apitraffic-request-log id="log" theme="light"></apitraffic-request-log>

<script>
  // Fetch a token from your backend
  fetch('/api/embed-token')
    .then(res => res.json())
    .then(({ token }) => {
      document.getElementById('log').token = token;
    });
</script>
Or with ES module imports:
import '@apitraffic/embed';

const el = document.querySelector('apitraffic-request-log');
el.token = await getTokenFromYourBackend();

Attributes

AttributeTypeDefaultDescription
tokenstringRequired. The signed embed JWT.
themestring'light''light', 'dark', or 'auto'.
api-urlstring'https://api.apitraffic.io/v1'Override the API base URL (self-hosted).
page-sizenumber25Requests per page (1–100).
show-detailstring'true'Set to 'false' to disable row expansion.

Events

const el = document.querySelector('apitraffic-request-log');

// Fires after the first successful data load
el.addEventListener('apitraffic:ready', () => {
  console.log('Request log is ready');
});

// Fires on any API error
el.addEventListener('apitraffic:error', (e) => {
  console.error('Error:', e.detail.message);
});

// Fires ~30 seconds before the token expires
el.addEventListener('apitraffic:token-expired', async () => {
  const { token } = await fetch('/api/embed-token').then(r => r.json());
  el.token = token; // component re-fetches automatically
});

// Fires when a request row is clicked
el.addEventListener('apitraffic:request-click', (e) => {
  console.log('Clicked request:', e.detail.requestSid);
});

React

Installation

npm install @apitraffic/embed-react @apitraffic/embed

Drop-In Component

import { RequestLog } from '@apitraffic/embed-react';

function ApiLog({ token }) {
  return (
    <RequestLog
      token={token}
      theme="light"
      pageSize={25}
      onTokenExpired={async () => {
        const res = await fetch('/api/embed-token');
        const { token } = await res.json();
        return token; // returning the token auto-refreshes
      }}
      onReady={() => console.log('Loaded')}
      onError={(e) => console.error(e.detail)}
      onRequestClick={(e) => console.log(e.detail.requestSid)}
    />
  );
}

Props

PropTypeDefaultDescription
tokenstringRequired. The signed embed JWT.
themestring'light''light', 'dark', or 'auto'.
apiUrlstringOverride the API base URL.
pageSizenumber25Requests per page.
showDetailbooleantrueEnable/disable expandable row detail.
onTokenExpiredfunctionReturn a new token string to auto-refresh.
onReadyfunctionFires after the first successful load.
onErrorfunctionFires on API errors.
onRequestClickfunctionFires when a row is clicked.

Headless Hook

Use useApiTrafficEmbed when you want full control over rendering:
import { useApiTrafficEmbed } from '@apitraffic/embed-react';

function CustomLog({ token }) {
  const {
    requests,
    loading,
    loadingMore,
    error,
    hasMore,
    loadMore,
    refresh
  } = useApiTrafficEmbed({
    token,
    pageSize: 50,
    onTokenExpired: async () => {
      const res = await fetch('/api/embed-token');
      return (await res.json()).token;
    }
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <button onClick={refresh}>Refresh</button>
      {requests.map(req => (
        <div key={req.sid}>
          {req.request?.method} {req.request?.path}{req.response?.statusCode}
        </div>
      ))}
      {hasMore && (
        <button onClick={loadMore} disabled={loadingMore}>
          {loadingMore ? 'Loading...' : 'Load More'}
        </button>
      )}
    </div>
  );
}

Theming

The Web Component uses CSS custom properties for theming. Override them on the host element:
apitraffic-request-log {
  /* Typography */
  --at-font-family: 'Inter', sans-serif;
  --at-font-size: 13px;

  /* Colors */
  --at-bg-color: #ffffff;
  --at-text-color: #1f2937;
  --at-text-muted: #6b7280;
  --at-accent-color: #3b82f6;
  --at-danger-color: #ef4444;

  /* Layout */
  --at-border-color: #e5e7eb;
  --at-border-radius: 6px;
  --at-row-hover-bg: #f9fafb;
  --at-max-height: 600px;
}

Available CSS Custom Properties

PropertyDefaultDescription
--at-font-familySystem font stackFont family
--at-font-size13pxBase font size
--at-bg-color#ffffffContainer background
--at-text-color#1f2937Primary text color
--at-text-muted#6b7280Secondary/muted text
--at-accent-color#3b82f6Links, buttons, GET method
--at-danger-color#ef4444Errors, DELETE method
--at-border-color#e5e7ebBorder color
--at-border-radius6pxContainer border radius
--at-row-hover-bg#f9fafbRow hover background
--at-max-heightnoneMax height (set for scrolling)

Dark Mode

Set theme="dark" for a built-in dark theme, or theme="auto" to follow the user’s OS preference via prefers-color-scheme.

Token Refresh Flow

Embed tokens are short-lived by design. The SDK fires a token-expired event approximately 30 seconds before expiry, giving your app time to fetch a fresh token seamlessly:

Security

All filters (bucket, environment, criteria, stream view) are cryptographically signed into the JWT using HMAC-SHA256. The client SDK reads the token to display data but cannot modify the filters. Any tampering invalidates the signature.
No. The embed data endpoints extract the accountSid, bucketSid, and all filters directly from the verified JWT payload. Query parameters from the client are ignored for filtering — only pagination cursors (from, limit) are accepted.
Tokens are short-lived (default 1 hour, max 24 hours) and scoped to a specific bucket and filter set. An attacker can only see the same data the embed was designed to show, and only until the token expires. You can also rotate the signing secret by calling the token endpoint — a new secret is auto-generated if the existing one is cleared.
No. Your API token is only used server-side to generate embed JWTs. The browser only ever sees the embed JWT, which cannot be used to access any other ApiTraffic API endpoints.

API Reference

Create Embed Token

POST /v1/accounts/{accountSid}/embeds/token
Authentication: Bearer token (API token) Request Body:
{
  "bucketSid": "string (required)",
  "view": "request-log",
  "filters": {
    "environmentSid": "string",
    "criteria": "string",
    "streamViewSid": "string"
  },
  "ttl": 3600,
  "theme": "light"
}
Response:
{
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "expiresAt": "2025-03-17T05:27:00.000Z"
}

Get Embed Requests

GET /v1/embed/requests?from={cursor}&limit={25}
Authentication: Bearer token (embed JWT)

Get Embed Request Detail

GET /v1/embed/requests/{requestSid}
Authentication: Bearer token (embed JWT)

Troubleshooting

Verify the bucketSid in your token request matches a bucket that has traffic. Check that any filters aren’t too restrictive.
The embed token has likely expired. Implement the apitraffic:token-expired event handler (or onTokenExpired prop in React) to auto-refresh.
The component uses Shadow DOM, so your page styles should not affect it. If you see issues, ensure you’re setting CSS custom properties on the apitraffic-request-log element itself, not inside its shadow root.
Ensure the SDK script or import is loaded before the element appears in the DOM. With the CDN approach, place the <script> tag before the component. With bundlers, ensure import '@apitraffic/embed' is called.