Embed the request log in your app

Embed an ApiTraffic request log directly into your application, with per-token control over which requests and request details it can show.

This page as Markdown

Give your customers or internal teams a real-time view of API traffic without leaving your app. To embed a saved report or dashboard instead (usage, errors and latency per customer), see Embedded Reports and Dashboards. The embed SDK renders a fully styled, CSS-isolated request log using Web Components (Shadow DOM) - no iFrames, no style conflicts.

Note: The embed feature requires an Administrator API token on your server to generate signed embed tokens. The ingest token your SDK sends traffic with can’t mint them. The client-side SDK never sees your API token.

Warning: The token that mints embed tokens is a read credential to your captured traffic. An Administrator API token can mint embed tokens for any bucket in its account, with any filters or none, so whoever holds it can read all of the account’s captured traffic through an embed. Store it as a secret (never in browser code, container images or a repository), and rotate it immediately if it may have been exposed. Deleting it stops it minting embed tokens; embed tokens it already minted stay valid until they expire (ttl, at most 24 hours).

Before you begin

  • Plan. Embeds need a plan that includes Embeds. If your plan stops including them, new embed tokens are refused and embeds already on your pages stop loading.
  • Credentials. Your server needs an API token with the Administrator role to mint embed tokens. Ingest and User tokens are refused with 403 and code 2010. With the Node SDK, set it as API_TRAFFIC_EMBED_TOKEN.
  • Role. Only account owners and admins can create that token, and only they can generate a preview in the app.

Try it in the app

Embed Setup builds an embed token from the options you pick, shows the embed live, and gives you code to copy. Generating the preview mints a real embed token, so it needs an owner or admin. Members can open the page and see the options, but Generate Token & Preview is turned off for them, with the notice “Only account owners and admins can change this.”

Open Embed Setup

Open the account menu, choose Account, then select Embed under Integration.

Configure the embed

Under Configuration, choose the Bucket, and optionally an Environment. Pick a Theme (Light, Dark or Auto) and a TTL (seconds) between 60 and 86400. To narrow the requests, enter a Stream View SID or Criteria, such as method == "GET". Choose the Exposed Tabs and Options (Copy request as code, Include request/response bodies).

Choosing what the embed shows.

Embed Setup configuration with bucket, environment, theme, TTL, stream view, criteria, exposed tabs and options

Preview it

Select Generate Token & Preview. The Live Preview shows the request log exactly as your users would see it.

The embed, previewed with a real token.

Live Preview of the embedded request log with one request expanded

Copy the code

Quick Start Code has snippets on the HTML / Vanilla JS, React and Server-Side (Node.js) tabs. In production, mint tokens on your server as shown below; never put an API token in browser code.

How it works

mermaid
sequenceDiagram
    participant Browser as Client Browser
    participant Server as Your Server
    participant AT as ApiTraffic API

    Browser->>Server: GET /api/embed-token
    Server->>AT: POST /v1/accounts/{id}/embeds/token
    AT-->>Server: { token, expiresAt }
    Server-->>Browser: { token }
    Browser->>AT: GET /v1/embed/requests (Bearer token)
    AT-->>Browser: Request log data
  1. Your backend calls the ApiTraffic API with an Administrator API token to generate a signed embed JWT - one call, apitraffic.embedToken(), if you already run the Node SDK.
  2. The JWT locks in the bucket, filters, theme, and which request detail tabs the embed may show - the client SDK cannot tamper with any of it.
  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.

The tab list is not a display setting. Every field of a request belongs to the tab that needs it, and the embed endpoints rebuild each response from that list - so a tab you do not grant contributes no data to the payload at all, rather than being hidden in the browser.

Generate an embed token on your server

If you already run an ApiTraffic Node SDK, it mints the token for you. Set an Administrator API token as API_TRAFFIC_EMBED_TOKEN; the host, bucket and account come from the SDK you configured, so the call names only what is specific to this embed. The raw HTTP call is unchanged and still fully supported; use it from any language, or from a service that does not run the SDK.

ApiTraffic SDK

javascript
const apitraffic = require('@apitraffic/node');

// Serve this from your own authenticated route (requireUser is your app's
// auth middleware). The browser gets the embed JWT; your API token never
// leaves the server.
app.get('/api/embed-token', requireUser, async (req, res) => {
  try {
    const { token, expiresAt } = await apitraffic.embedToken({
      filters: {
        // Scope the embed to this customer's traffic. Signed into the token,
        // so the browser cannot widen it.
        // Written into the criteria as tag:customerId == "…", with the id escaped:
        // a value such as `x" OR method == "GET` cannot add a condition. If you
        // build a criteria string yourself, use apitraffic.escapeCriteriaValue().
        tags: { customerId: req.user.id }
      },
      ttl: 900,                                  // seconds (min 60, max 86400)
      theme: 'light',                            // 'light' | 'dark' | 'auto'

      // Which request detail tabs this embed may show.
      // Omit for the default set; data behind any tab left out is never returned.
      tabs: ['request', 'response', 'connection', 'tags', 'ai'],

      features: {
        copyRequest: true,                       // offer "Copy request" as code
        includeBodies: false                     // withhold request/response bodies
      }
    });

    res.json({ token, expiresAt });
  } catch (error) {
    // This call rejects rather than going quiet - a token has one useful
    // outcome, so a failure is a 500 on your route, not a blank embed.
    res.status(502).json({ error: error.message });
  }
});

The same function is exported by every ApiTraffic Node package - @apitraffic/node, @apitraffic/express, @apitraffic/fastify, @apitraffic/koa and @apitraffic/hapi - so whichever one you already installed can mint tokens.

Node.js (HTTP)

javascript
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 stream view
      },
      ttl: 3600,                               // seconds (min 60, max 86400)
      theme: 'light',                          // 'light' | 'dark' | 'auto'

      // Which request detail tabs this embed may show.
      // Omit for the default set; data behind any tab left out is never returned.
      tabs: ['request', 'response', 'connection', 'tags', 'ai'],

      features: {
        copyRequest: true,                     // offer "Copy request" as code
        includeBodies: false                   // withhold request/response bodies
      }
    },
    {
      headers: { Authorization: `Bearer ${apiToken}` }
    }
  );

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

Python

python
import requests

def get_embed_token(account_sid, api_token):
    response = requests.post(
        f"https://api.apitraffic.io/v1/accounts/{account_sid}/embeds/token",
        json={
            "bucketSid": "your-bucket-sid",
            "view": "request-log",
            "filters": {
                "environmentSid": "abc123",
                "criteria": 'method == "GET"'
            },
            "ttl": 3600,
            "theme": "light",

            # Which request detail tabs this embed may show. Omit for the
            # default set; data behind any tab left out is never returned.
            "tabs": ["request", "response", "connection", "tags", "ai"],
            "features": {
                "copyRequest": True,
                "includeBodies": False
            }
        },
        headers={"Authorization": f"Bearer {api_token}"}
    )
    return response.json()  # { "token": ..., "expiresAt": ..., "tabs": [...], "features": {...} }

cURL

bash
curl -X POST "https://api.apitraffic.io/v1/accounts/acc_.../embeds/token" \
  -H "Authorization: Bearer YOUR_ADMINISTRATOR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "bucketSid": "your-bucket-sid",
    "filters": { "criteria": "method == \"GET\"" },
    "ttl": 3600,
    "theme": "light",
    "tabs": ["request", "response", "connection", "tags", "ai"],
    "features": { "copyRequest": true, "includeBodies": false }
  }'

What the SDK fills in

embedToken() takes the same parameters as the HTTP endpoint, but only asks for the ones the SDK cannot already answer:

Parameter Where it comes from when you omit it
bucketSid The bucket this process reports to (bucket / API_TRAFFIC_BUCKET).
accountSid The SDK settings, which name the account the API token belongs to.
API token API_TRAFFIC_EMBED_TOKEN, then the SDK’s own token (token / API_TRAFFIC_TOKEN). The SDK’s own token is normally an ingest token, which is refused.
API host The SDK’s own host - set this only if you are self-hosting.

Each can still be passed per call (bucketSid, accountSid, token, apiHost) to mint a token for a bucket or account other than the one this process reports to. A token passed to the call takes precedence over API_TRAFFIC_EMBED_TOKEN.

Warning: If API_TRAFFIC_EMBED_TOKEN isn’t set and you don’t pass token, embedToken() falls back to the SDK’s own token. That is normally an ingest token, so the API answers 403, and the error tells you to set API_TRAFFIC_EMBED_TOKEN. Create an Administrator API token and set it as API_TRAFFIC_EMBED_TOKEN on the servers that mint embed tokens.

Note: The account is learned from the SDK settings, which load shortly after the middleware mounts. In a process that generates tokens but does not run the ApiTraffic middleware, set API_TRAFFIC_ACCOUNT_SID or pass accountSid to the call.

Token parameters

Parameter Type Required Default Description
bucketSid string Yes¹ - The bucket to scope the embed to.
view string No 'request-log' The embed view type.
filters object No {} Filters locked into the token (see below).
ttl number No 3600 Token lifetime in seconds (60–86400).
theme string No 'light' Theme preference: 'light', 'dark', or 'auto'.
tabs array No See below The request detail tabs this embed may show.
features object No {} Optional behaviors that are not tabs (see below).

¹ Required on the HTTP endpoint. embedToken() defaults it to the bucket the SDK reports to.

Filter options

Filter Type Description
environmentSid string Filter to a specific environment.
criteria string Search criteria string (same syntax as the API). Conditions are joined with AND; a token with OR between conditions is refused.
streamViewSid string Use a stream view’s filter criteria.

Warning: 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.

Warning: Protect the route that mints tokens. An embed token is a read credential for the traffic it is scoped to, so the endpoint on your server that creates one is as sensitive as that data:

  • Require your user’s sign-in on it. Never mint a token for an anonymous caller.
  • Decide filters, tabs, features and ttl on the server, from the signed-in user. Never copy them from the request body or query string.
  • Keep ttl short (minutes, not hours); the component asks for a fresh token before expiry.

The embed SDK’s sample app (examples/sample-app/server.js) shows all three.


Request detail tabs

Clicking a row expands the same request detail you see in the ApiTraffic dashboard. The tabs array on the token decides which of those tabs the embed can show - and, just as importantly, which data the API will return for it.

Tab Shows
request Request line, headers and query string.
response Status, headers and content type.
connection Per-phase connection timings.
waf What the application firewall did with the request.
related Other requests sharing this request’s context.
workflows Workflow executions the request triggered.
tags The request’s tags.
waterfall APM spans, drawn as a waterfall.
traces Trace identifiers carried on the request.
trace Every request sharing this request’s distributed trace.
concurrency What else the request’s server was doing at the time.
ai An AI fix prompt for the failure, with correlated deploys.

The default set - used when you omit tabs entirely - is the group that describes the request itself:

json
["request", "response", "connection", "workflows", "tags", "traces", "ai"]

Pass tabs: [] to hide the request detail completely, leaving a read-only list of rows.

Note: Some tabs only appear when they apply to the request in front of the reader, exactly as in the dashboard. connection needs per-phase timings on the request, ai is offered only on failures (4xx/5xx), and captures - requests with no response - show only the request tab.

Tabs that reach beyond the request

Four tabs answer with data from outside the single request being viewed. related, trace and concurrency are off by default; ai is in the default set. Think about each before you grant it to an audience that is not your own team:

Warning: - related and trace follow the request into other traffic in the same bucket - including requests your token’s criteria filter would otherwise exclude.

  • concurrency lists requests that ran on the same server, which can include traffic from your other buckets.
  • ai includes the deploys correlated with the failure: repository names, branches and commit messages from your Git integration.

Features

Behaviors that are not tabs, passed as features on the token:

Feature Default Description
copyRequest false Adds a Copy request button that generates the request as ready-to-run code: cURL, JavaScript (fetch and axios), Python, PHP, Go, Ruby, Java and C#. Exposes the request headers and query string even when the request tab is not shown.
includeBodies false Returns request and response bodies for the request and response tabs.

Note: Bodies are withheld even when the request and response tabs are granted, unless includeBodies is set. They are the likeliest place for something an embed’s audience should not see, so showing them is a separate, deliberate decision.

When bodies are shown, JSON renders as a collapsible tree - the first two levels open, everything below a click away - with a Raw toggle for selecting and copying the payload as text. Bodies that are not JSON render as plain text.


Vanilla JavaScript

Installation

npm

bash
npm install @apitraffic/embed

yarn

bash
yarn add @apitraffic/embed

CDN (no build step)

bash
<script src="https://unpkg.com/@apitraffic/[email protected]/dist/apitraffic-embed.iife.js" integrity="sha384-<hash of that exact file>" crossorigin="anonymous"></script>

Basic usage

html
<!-- CDN approach -->
<script src="https://unpkg.com/@apitraffic/[email protected]/dist/apitraffic-embed.iife.js" integrity="sha384-<hash of that exact file>" crossorigin="anonymous"></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 }) => {
      // A property, not an attribute: the component keeps the token
      // privately and it never appears in the DOM.
      document.getElementById('log').token = token;
    });
</script>

Or with ES module imports:

javascript
import '@apitraffic/embed';

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

Attributes

Attribute Type Default Description
token string - Required. The signed embed JWT. Set it as the token property; the attribute still works but exposes the token to every script on the page. el.token = null clears the log.
theme string 'light' 'light', 'dark', or 'auto'.
api-url string 'https://api.apitraffic.io/v1' Override the API base URL (self-hosted).
page-size number 25 Requests per page (1–100).
show-detail string 'true' Set to 'false' to disable row expansion.

Note: There is deliberately no attribute for the tabs: the token decides, so a viewer editing the DOM cannot reveal one. Once data has loaded, the resolved list is readable at element.tabs and element.features.

Events

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

// Fires after the first successful data load, reporting what the token exposes
el.addEventListener('apitraffic:ready', (e) => {
  console.log('Tabs:', e.detail.tabs);         // e.g. ['request', 'response', 'ai']
  console.log('Features:', e.detail.features); // { copyRequest: true, includeBodies: false }
});

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

// Fires ~30 seconds before the token expires, or when the API rejects it.
// e.detail.reason is 'expiring', 'expired' or 'unauthorized'.
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);
});

Server-side rendering

Importing @apitraffic/embed during a server render (Next.js, Remix, Nuxt, plain Node) is safe: outside a browser it registers nothing and touches no global. The element is registered when the module loads in the browser, and nothing is fetched until it is on the page with a token.

Rendering your own UI instead? @apitraffic/embed/headless exports EmbedApi and the token helpers without the Web Component, so importing it registers no custom element at all.

Content Security Policy

The component runs under a strict policy with no 'unsafe-inline'. Its styles are a constructed stylesheet adopted by its shadow root, and its geometry is set through the CSSOM, so it needs only:

text
connect-src https://api.apitraffic.io

(or the origin of your api-url), plus script-src for wherever the script itself comes from - for the CDN build, https://unpkg.com. Browsers without constructable stylesheets (Safari before 16.4) fall back to a `` element; under a strict policy, give it a nonce by setting nonce="..." on <apitraffic-request-log>.

Accessibility

Rows are keyboard-operable (Enter / Space) and report aria-expanded; the detail tabs are a WAI-ARIA tablist (arrow keys, Home, End); loads and errors are announced through a polite live region; focus rings are kept for keyboard users and can be recolored with --at-focus-color.


React

Installation

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

Both exports are client components ('use client') with ESM, CommonJS and TypeScript builds, and both are safe to import during server rendering: `` renders the bare element on the server and loads the Web Component in the browser, and the token is never rendered into the HTML.

Drop-in component

jsx
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

Prop Type Default Description
token string - Required. The signed embed JWT.
theme string 'light' 'light', 'dark', or 'auto'.
apiUrl string - Override the API base URL.
pageSize number 25 Requests per page.
showDetail boolean true Enable/disable expandable row detail.
onTokenExpired function - Return a new token string to auto-refresh. Never called concurrently.
onReady function - Fires after the first successful load.
onError function - Fires on API errors, and with detail.code: 'TOKEN_REFRESH_FAILED' when onTokenExpired throws.
onRequestClick function - Fires when a row is clicked.

Note: There is no tabs prop - tabs come from the token, for the same reason filters do. onReady receives the resolved list as e.detail.tabs and e.detail.features.

Headless hook

Use useApiTrafficEmbed when you want full control over rendering. It registers no custom element, cancels its requests on unmount or when token, apiUrl or pageSize change, and drops late answers for an old token:

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

function CustomLog({ token }) {
  const {
    requests,
    config,        // { view, theme, tabs, features } - what the token exposes
    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>;

  // Records only carry the fields the token's tabs allow, so check `config`
  // before rendering anything beyond the row itself.
  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:

css
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

Property Default Description
--at-font-family System font stack Font family
--at-font-size 13px Base font size
--at-bg-color #ffffff Container background
--at-text-color #1f2937 Primary text color
--at-text-muted #6b7280 Secondary/muted text
--at-accent-color #3b82f6 Links, buttons, GET method
--at-danger-color #ef4444 Errors, DELETE method
--at-border-color #e5e7eb Border color
--at-border-radius 6px Container border radius
--at-row-hover-bg #f9fafb Row hover background
--at-max-height none Max 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. It also fires when the API answers 401, which is the authoritative signal that a token is done.

The SDK never turns a token problem into a loop against your token endpoint:

  • Clock skew. Expiry is measured against the server’s clock, taken from the token’s iat. A visitor whose computer clock is hours fast still gets the token’s full lifetime, instead of every new token looking expired on arrival.
  • Backoff. While no load succeeds, each further token-expired waits twice as long as the previous one (1 s, 2 s, 4 s … up to 5 minutes).
mermaid
sequenceDiagram
    participant Component as Embed Component
    participant App as Your App
    participant Server as Your Server
    participant AT as ApiTraffic API

    Note over Component: Token expires in 30s
    Component->>App: apitraffic:token-expired event
    App->>Server: GET /api/embed-token
    Server->>AT: POST .../embeds/token
    AT-->>Server: { token, expiresAt }
    Server-->>App: { token }
    App->>Component: el.token = newToken
    Note over Component: Re-fetches data with new token

Security

How are filters protected?

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.

Can the client access data outside the filter scope?

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.

Reading a single request re-checks that scope rather than trusting the listing it came from: a request sid from another bucket, or from an environment the token is locked out of, reads as “not found” even though the sid is all the endpoint is given.

How do tabs limit what the embed can see?

The tab list is signed into the JWT alongside the filters, and enforced server-side in two places.

Every request the embed receives - both list rows and the expanded detail - is rebuilt field by field from the granted tabs. Without the request tab there are no request headers in the payload; without tags there are no tags; without waterfall there are no spans. The browser is never sent data it has been told not to display.

The four tabs backed by their own endpoints (related, trace, concurrency, ai) refuse outright unless their tab is granted, and answer as “not found” rather than “forbidden” - an embed’s viewer cannot probe for which tabs exist behind the ones they were given.

Are request and response bodies included?

Only if you ask for them. features.includeBodies defaults to false, so bodies are withheld even when the request and response tabs are shown. Redaction rules configured on your account still apply on top - bodies reach the embed already redacted.

What happens if a token is stolen?

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.

Is my API token exposed to the browser?

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

text
POST /v1/accounts/{accountSid}/embeds/token

Authentication: Bearer token (an Administrator API token, or a signed-in owner or admin). Ingest and User tokens, and members, are refused with 403 and code 2010.

Request Body:

json
{
  "bucketSid": "string (required)",
  "view": "request-log",
  "filters": {
    "environmentSid": "string",
    "criteria": "string",
    "streamViewSid": "string"
  },
  "ttl": 3600,
  "theme": "light",
  "tabs": ["request", "response", "connection", "workflows", "tags", "traces", "ai"],
  "features": {
    "copyRequest": false,
    "includeBodies": false
  }
}

Response:

json
{
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "expiresAt": "2025-03-17T05:27:00.000Z",
  "tabs": ["request", "response", "connection", "workflows", "tags", "traces", "ai"],
  "features": { "copyRequest": false, "includeBodies": false }
}

view also takes report and dashboard, with their own parameters: see Embedded reports and dashboards. A token works only on the endpoints of its own view (403 EMBED_VIEW_MISMATCH otherwise).

The returned tabs and features are the resolved values: unknown tabs are dropped, the order is normalized, and omitting tabs reports the defaults. Store or log these if you need a record of what a given embed was allowed to show.

Get embed requests

text
GET /v1/embed/requests?from={cursor}&limit={25}

Authentication: Bearer token (embed JWT)

Rows carry only the fields the granted tabs need, alongside the resolved config:

json
{
  "hasMore": false,
  "from": null,
  "config": {
    "view": "request-log",
    "theme": "light",
    "tabs": ["request", "response", "ai"],
    "features": { "copyRequest": false, "includeBodies": false }
  },
  "records": [ ... ]
}

Get embed request detail

text
GET /v1/embed/requests/{requestSid}

Authentication: Bearer token (embed JWT)

Returns the request filtered to the granted tabs, with the same config object attached.

text
GET /v1/embed/requests/{requestSid}/related

Authentication: Bearer token (embed JWT) · Requires: the related tab

Summary rows for the other requests sharing this request’s context.

Get distributed trace

text
GET /v1/embed/requests/{requestSid}/trace

Authentication: Bearer token (embed JWT) · Requires: the trace tab

Every request in this request’s distributed trace, in time order. traceId is null when the request is not part of one.

Get server activity

text
GET /v1/embed/requests/{requestSid}/concurrent?limit={25}&lookbackMinutes={5}

Authentication: Bearer token (embed JWT) · Requires: the concurrency tab

What else the request’s server was running at the time, summarized by endpoint and listed per request. Answers with available: false for traffic captured before server attribution.

Get AI fix prompt

text
GET /v1/embed/requests/{requestSid}/fix-prompt?lookbackMinutes={180}

Authentication: Bearer token (embed JWT) · Requires: the ai tab

A paste-ready prompt for an AI coding agent, with the deploys correlated to the failure.

Note: An endpoint whose tab is not granted responds as though the request does not exist. That is deliberate: it keeps an embed’s viewer from discovering which views were withheld.


Troubleshooting

Component shows ‘No requests found’

Verify the bucketSid in your token request matches a bucket that has traffic. Check that any filters aren’t too restrictive.

The token request is refused with 403

The token your server uses isn’t allowed to mint embed tokens. Only Administrator API tokens can; ingest tokens (the one your SDK sends traffic with) and User tokens get 403 with code 2010. With the Node SDK, the error says “Embed tokens need an Administrator API token: set API_TRAFFIC_EMBED_TOKEN, or pass token to embedToken().” Create an Administrator token under Account → API Tokens and set it as API_TRAFFIC_EMBED_TOKEN. A 403 with code 2221 means your plan doesn’t include Embeds.

A tab I expected is missing

Three things decide whether a tab appears. Check them in order:

  1. Is it on the token? Only tabs listed in tabs are rendered; omitting the array gives the default set, which excludes waf, related, trace, concurrency and waterfall. Read element.tabs (or e.detail.tabs from apitraffic:ready) to see what the token actually granted.
  2. Does it apply to this request? connection needs per-phase timings, ai appears only on 4xx/5xx responses, and captures show only the request tab.
  3. Is there data behind it? A granted tab with nothing to show says so - “No tags on this request”, “No spans were captured” - rather than disappearing.

Bodies show as empty on the Request or Response tab

Set features.includeBodies: true when generating the token. Bodies are withheld by default even when those tabs are granted. If they are still empty, check your account’s redaction rules and that body capture is enabled for the bucket.

401 errors in the browser console

The embed token has expired or was rejected. The component fires apitraffic:token-expired with detail.reason: 'unauthorized'; implement that handler (or the onTokenExpired prop in React) to auto-refresh. If the 401s continue, the tokens your endpoint returns are being rejected - check that it mints a new token each time rather than serving a cached one.

Styles are blocked by our Content Security Policy

Current browsers need no style-src allowance at all: the component adopts a constructed stylesheet. Only browsers without constructable stylesheets (Safari before 16.4) fall back to a `` element - set nonce="..." on <apitraffic-request-log> to the nonce your policy allows.

Styles look wrong or are overridden

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.

Component doesn’t render

Ensure the SDK script or import is loaded before the element appears in the DOM. With the CDN approach, place the `` tag before the component. With bundlers, ensure import '@apitraffic/embed' is called.