Skip to main content
Beta. pb.ir wraps the IR mapping REST surface (/v1/ir/mappings). It is live and tested; expect it to grow.
pb.ir records version-stamped matches between an external API’s types and your ontology’s types. You open a persistent mapping session, ingest an OpenAPI spec, record a match per type (with a relation and your reasoning), then analyze coverage and drift. See the concept guide for the vocabulary; this page is the SDK surface.

The fluent session

pb.ir.open returns a session whose methods read like the judgments you are making.
import { createPenumbra } from "@penumbra-systems/platform";

const pb = createPenumbra({ apiKey: process.env.PENUMBRA_API_KEY! });

const mapping = await pb.ir.open({ projectId, name: "Commerce API → CRM" });

// Ingest their spec into the symbol table.
await mapping.ingestOpenAPI(spec);

// Record a type-level match.
await mapping
  .type("#/components/schemas/Customer")
  .mapsTo("crm.Person", { relation: "identity" });

// Map a property under it.
await mapping
  .property("#/components/schemas/Customer", "email")
  .mapsTo("crm.Person.email", { role: "titular" });

// Record a deliberate non-mapping, on the record.
await mapping
  .type("#/components/schemas/Payment")
  .markDisjoint({ reasoning: "Out of scope for the CRM." });

// Coverage, property coverage, and drift.
const analysis = await mapping.analyze();

Session methods

MethodWhat it does
mapping.idThe mapping_id this session operates on.
mapping.ingestOpenAPI(spec, opts?)Ingest a parsed OpenAPI 3.x document.
mapping.ingestSource(input)Ingest a foreign IR by explicit input.
mapping.type(path)Begin a type mapping from a source path. Chain .mapsTo(toType, { relation }), or .markDisjoint({ reasoning }) to record it as deliberately out of scope.
mapping.property(path, fromProperty)Begin a property mapping. Chain .mapsTo(toProperty, { role }).
mapping.reviewProposals(input)Review model-proposed matches without mutating the mapping.
mapping.analyze(input?)Coverage, property coverage, drift, and a healthy roll-up.
mapping.inspect()Read the current mapping state.

One-shot mapping

When you already have the matches in hand, pb.ir.map runs open, ingest, type mappings, properties, and analyze in a single call.
const result = await pb.ir.map({
  projectId,
  name: "Commerce API → CRM",
  source: { spec },
  types: [
    {
      from: "#/components/schemas/Customer",
      to: "crm.Person",
      relation: "identity",
      properties: [
        { from: "email", to: "crm.Person.email", role: "titular" },
      ],
    },
  ],
});

Granular methods

The session is sugar over these. Reach for them when you want to drive the steps yourself, or resume a mapping by id.
MethodWhat it does
pb.ir.tools()List the IR tools and their REST routes.
pb.ir.open(input)Open a session and return a fluent IrMappingSession.
pb.ir.get(mappingId)Resume an existing mapping as a session.
pb.ir.map(input)Run a whole mapping in one call.
pb.ir.openMapping(input)Open a session, returning the raw result.
pb.ir.ingestSource(mappingId, input)Ingest a spec into a mapping.
pb.ir.mapType(mappingId, input)Map one type to another (or record disjoint).
pb.ir.unmapType(mappingId, input)Remove a type mapping.
pb.ir.mapProperty(mappingId, input)Map a property pair.
pb.ir.reviewProposals(mappingId, input)Review model-proposed matches.
pb.ir.inspect(mappingId)Read mapping state.
pb.ir.analyze(mappingId, input?)Coverage and drift.
pb.ir.dispatch(tool, args)Escape hatch: call any IR tool by name.
IR tool errors come back inside the result as isError: true, not as a thrown HTTP error. Check the result rather than relying on a catch.

Next

IR mapping guide

The vocabulary, a worked mapping, and drift detection.

Decision quality

Verify a region of the graph is fit to act on.