Magic Links

Magic links generate short-lived portal session tokens, ideal for:

  • Embedding the hosted portal in an iframe without exposing long-lived tokens
  • Generating one-click links in emails or dashboards
  • Time-boxed portal access for support scenarios

How It Works

  1. Your backend calls the magic link endpoint with an application ID
  2. Hookbase returns a URL like https://www.hookbase.app/portal/whpt_abc123... and the raw token
  3. You redirect your customer to the URL or embed it in an iframe
  4. The token expires automatically (default: 60 minutes, max: 24 hours)

API Endpoint

POST /api/organizations/:orgId/portal/webhook-applications/:appId/magic-link

Request Body

FieldTypeDefaultDescription
expiresInMinutesnumber60Token lifetime (1–1440 minutes)
scopesstring[]['read', 'write']Permissions: read, write, or both

Response

{
  "data": {
    "url": "https://www.hookbase.app/portal/whpt_abc123...",
    "token": "whpt_abc123...",
    "expiresAt": "2026-03-14T15:30:00.000Z",
    "expiresInMinutes": 60,
    "scopes": ["read", "write"]
  }
}

Server-Side Examples

Node.js / Express

app.get('/api/portal-link', async (req, res) => {
  const customer = await getCustomerFromSession(req);
 
  const response = await fetch(
    `https://api.hookbase.app/api/organizations/${ORG_ID}/portal/webhook-applications/${customer.appId}/magic-link`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${API_KEY}`,
      },
      body: JSON.stringify({
        expiresInMinutes: 60,
        scopes: ['read', 'write'],
      }),
    }
  );
 
  const { data } = await response.json();
  res.json({ url: data.url });
});

Python / Flask

@app.route('/api/portal-link')
def portal_link():
    customer = get_customer_from_session()
 
    response = requests.post(
        f'https://api.hookbase.app/api/organizations/{ORG_ID}/portal/webhook-applications/{customer.app_id}/magic-link',
        headers={
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {API_KEY}',
        },
        json={
            'expiresInMinutes': 60,
            'scopes': ['read', 'write'],
        },
    )
 
    data = response.json()['data']
    return jsonify({'url': data['url']})

Using the Token with the npm Package

If you prefer to render the portal in your own React app rather than using the hosted page, pass the magic link token directly to <HookbasePortal>:

function PortalPage() {
  const [token, setToken] = useState<string | null>(null);
 
  useEffect(() => {
    fetch('/api/portal-link')
      .then(res => res.json())
      .then(data => {
        // Extract token from URL or use the raw token
        setToken(data.token);
      });
  }, []);
 
  if (!token) return <div>Loading...</div>;
 
  return (
    <HookbasePortal token={token}>
      <EndpointList />
      <MessageLog />
    </HookbasePortal>
  );
}

Iframe Embedding

function PortalEmbed() {
  const [portalUrl, setPortalUrl] = useState<string | null>(null);
 
  useEffect(() => {
    fetch('/api/portal-link')
      .then(res => res.json())
      .then(data => setPortalUrl(data.url));
  }, []);
 
  if (!portalUrl) return <div>Loading...</div>;
 
  return (
    <iframe
      src={portalUrl}
      width="100%"
      height="600"
      style=
      title="Webhook Portal"
    />
  );
}

Security Best Practices

  • Always generate magic links server-side. Never expose your API key to the browser.
  • Use the shortest expiration that works. Default 60 minutes is good for most use cases.
  • Scope tokens to read when write access isn't needed. Read-only tokens prevent customers from creating or modifying endpoints.
  • Don't cache or reuse magic link tokens. Generate a fresh one each time the user opens the portal.
  • Use HTTPS for iframe embeds. Mixed content will be blocked by browsers.
Long-Lived TokenMagic Link
Lifetime1–365 days1–1440 minutes
Use casenpm package with token stored in your backendHosted portal links, iframe embeds
EndpointPOST .../tokensPOST .../magic-link
Returns URLNo (token only)Yes (hookbase.app/portal/{token})
Session tokenNoYes (excluded from token list)