Skip to content
Dark MatterDark Matter
Cookbook

Extension Position Lookup

Chrome extension side panel with position lookup from current page.

Overview

This cookbook shows how to build a Chrome extension side panel that performs position lookup from the current page context. When you view a company, deal, or fund on supported research platforms (Bloomberg, PitchBook, AlphaSense), the extension extracts entity identifiers from the DOM, queries the DMTR API via a background script, and displays position details — valuation, transfer status, restrictions — in the side panel. The pattern powers the official DMTR extension and can be adapted for custom sites.

The flow is: content script extracts context → background script queries DMTR → side panel renders results. Credentials stay in the background script; content scripts never see API keys.

Step 1: Configure the extension

Create a Chrome extension with a side panel, content scripts for supported sites, and a background service worker. The manifest declares all three.

{
  "manifest_version": 3,
  "name": "DMTR Position Lookup",
  "version": "1.0.0",
  "permissions": ["sidePanel", "storage", "scripting"],
  "host_permissions": ["https://api.dmtr.ai/*", "https://www.bloomberg.com/*", "https://pitchbook.com/*", "https://*.alphasense.com/*"],
  "background": { "service_worker": "background.js" },
  "side_panel": { "default_path": "sidepanel.html" },
  "content_scripts": [
    {
      "matches": ["https://www.bloomberg.com/*", "https://pitchbook.com/*", "https://*.alphasense.com/*"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}

Step 2: Extract page context

The content script parses the DOM on supported sites to find company names, deal IDs, or entity references. Each site has a different structure; the example below shows selectors for Bloomberg and PitchBook. Add similar extractors for AlphaSense and Carta as needed.

// content.ts — entity extraction for Bloomberg and PitchBook

function extractFromBloomberg(): { issuerName: string } | null {
  const el = document.querySelector("[data-type='company-name'], .security-name");
  const name = el?.textContent?.trim();
  return name ? { issuerName: name } : null;
}

function extractFromPitchBook(): { issuerName: string } | null {
  const el = document.querySelector("h1.company-name, .entity-header__name, [data-testid='company-name']");
  const name = el?.textContent?.trim();
  return name ? { issuerName: name } : null;
}

export function extractEntityFromPage(): { issuerName: string } | null {
  const host = window.location.hostname;
  if (host.includes("bloomberg.com")) return extractFromBloomberg();
  if (host.includes("pitchbook.com")) return extractFromPitchBook();
  return null;
}

Step 3: Query DMTR

The background script holds the API key and performs the DMTR request. When the side panel requests a lookup, the background script receives the extracted issuer_name, calls the positions API, and returns the result.

// background.ts — runs in service worker context

chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
  if (msg.type === "LOOKUP_POSITION" && msg.issuerName) {
    lookupPosition(msg.issuerName).then(sendResponse);
    return true; // async response
  }
});

async function lookupPosition(issuerName: string) {
  const { apiKey } = await chrome.storage.session.get("apiKey");
  if (!apiKey) return { error: "Not authenticated" };

  const url = "https://api.dmtr.ai/v1/positions?issuer_name=" + encodeURIComponent(issuerName);
  const res = await fetch(url, {
    headers: { Authorization: "Bearer " + apiKey }
  });
  const data = await res.json();
  return res.ok ? data : { error: data.message || "Lookup failed" };
}
200Success
{
  "data": [
    {
      "entity_id": "meg-2026-112",
      "issuer_name": "Acme Growth Fund III",
      "asset": {
        "vehicle_class": "fund_lp",
        "issuer": "Acme Growth Fund III",
        "terms_hash": "sha256:abc123..."
      },
      "valuation": {
        "current_mark": 2500000,
        "currency": "USD"
      },
      "status": {
        "current": "active"
      },
      "transfer": {
        "transferable": true,
        "restrictions": [
          "lockup",
          "rofr",
          "gp_consent"
        ]
      },
      "compliance": {
        "kyc_status": "cleared",
        "aml_status": "cleared"
      }
    }
  ],
  "has_more": false
}

Step 4: Render in side panel

The side panel is a React (or vanilla) UI that requests page context from the content script, forwards it to the background for the DMTR lookup, and displays the position summary.

// SidePanel.tsx — React component in side panel

function PositionSummary({ position }: { position: Position }) {
  return (
    <div className="space-y-2">
      <h3>{position.asset?.issuer ?? position.issuer_name}</h3>
      <p>Status: {position.status?.current}</p>
      <p>Valuation: ${(position.valuation?.current_mark ?? 0).toLocaleString()}</p>
      <p>Transferable: {position.transfer?.transferable ? "Yes" : "No"}</p>
      {position.transfer?.restrictions?.length ? (
        <p>Restrictions: {position.transfer.restrictions.join(", ")}</p>
      ) : null}
      <a href={"https://console.dmtr.ai/positions/" + position.entity_id} target="_blank">
        View in DMTR →
      </a>
    </div>
  );
}

// Request context from content script, then lookup via background
const [tabId] = await chrome.tabs.query({ active: true, currentWindow: true });
const context = await chrome.tabs.sendMessage(tabId, { type: "GET_PAGE_CONTEXT" });
if (context?.issuerName) {
  const result = await chrome.runtime.sendMessage({ type: "LOOKUP_POSITION", issuerName: context.issuerName });
  setPosition(result?.data?.[0] ?? null);
}

Never store API keys in content scripts. Content scripts run in the page context and can be inspected by the page or extensions. Store credentials in the background service worker and use chrome.runtime.sendMessage to request lookups. See Extension Permissions and Data Handling for security guidance.

  • Extension Overview — Architecture and features of the DMTR extension
  • Supported Sites — DOM selectors and extraction patterns for Bloomberg, PitchBook, AlphaSense, Carta
Edit this page on GitHub

Last reviewed: March 13, 2026

Was this page helpful?