Skip to main content
After the Quickstart, this guide covers the full recording API: entity types, action types, inline attributions, standalone attributions, session linking, and batch recording.

Entities

The entity() method

const id = await pk.entity({
  role: string;           // "human" | "ai" | "organization" | any custom string
  name?: string;
  publicKey?: string;     // ed25519 public key — enables signed attributions
  aiAgent?: {
    model: { provider: string; model: string; version?: string };
    delegatedBy?: string;           // entity ID of the human who delegated
    autonomyLevel?: "autonomous" | "supervised" | "assistive";
  };
});
publicKey is optional but required if you want to verify signed attributions. Use @provenancekit/sdk/signing to produce keypairs and sign attributions.

Entity deduplication

The API deduplicates entities by a hash of (role, name, publicKey). Calling entity() twice with identical fields returns the same ID — safe to call on every request.
const id1 = await pk.entity({ role: "ai", name: "GPT-4o" });
const id2 = await pk.entity({ role: "ai", name: "GPT-4o" });
// id1 === id2

Recording actions with file()

file() is the primary method for recording an action that produces a content-addressed output.
const actionId = await pk.file({
  // Required
  type: string;          // action type — "file.create", "model.infer", "remix", etc.
  performedBy: string;   // entity ID of the primary actor
  cid: string;           // content-addressed ID of the primary output

  // Optional
  inputs?: Array<{ cid: string }>;   // upstream CIDs this action consumed
  sessionId?: string;                // groups actions into a session timeline
  extensions?: Record<string, unknown>;
  attributions?: Array<{             // inline attributions (shorthand)
    entityId: string;
    role: string;
    confidence?: number;             // 0–1
  }>;

  // On-chain anchoring (requires chainAdapter set on client)
  // Result includes: actionId.onchain.txHash, .chainId, .contractAddress
});

Action types

Action types are freeform strings. Recommended conventions:
TypeWhen to use
file.createAny AI-generated or human-created content
file.editA modification to an existing CID
model.inferA model inference call (without a persistent output CID)
remixDerivative work from one or more source CIDs
reviewA human review or approval action
publishContent was published or made public

Attributions

Inline attributions (with file())

Pass attributions in the file() call for the common case of recording who contributed to an action:
await pk.file({
  type: "file.create",
  performedBy: aiId,
  cid: outputCid,
  attributions: [
    { entityId: humanId, role: "prompter", confidence: 1.0 },
    { entityId: reviewerId, role: "reviewer", confidence: 1.0 },
  ],
});

Standalone attributions

Use pk.attribution() when you need to add an attribution after the fact, or when attributing to a CID rather than an action:
await pk.attribution({
  entityId: humanId,
  actionId: "act_abc123",     // attribute to an action
  // resourceRef: "bafy...",  // OR attribute to a CID directly
  role: "approver",
  confidence: 0.95,
  extensions: {
    "ext:license@1.0.0": {
      spdxId: "CC-BY-4.0",
      aiTraining: "prohibited",
    },
  },
});

Sessions

A sessionId groups related actions into a timeline — useful for conversations, pipeline runs, or creative sessions.
const sessionId = crypto.randomUUID(); // generate once per session

// Every action in the session passes the same sessionId
await pk.file({ ..., sessionId });
await pk.file({ ..., sessionId });
await pk.file({ ..., sessionId });

// Query all actions in a session
const sessionActions = await pk.listActions({ sessionId });
The ProvenanceTracker UI component from @provenancekit/ui renders a session timeline.

Querying

// Full provenance bundle for a CID (entities + actions + attributions)
const bundle = await pk.getBundle(cid);

// Single action
const action = await pk.getAction(actionId);

// List with filters
const actions = await pk.listActions({
  type: "file.create",
  performedBy: entityId,
  sessionId: "sess_abc",
  limit: 50,
  offset: 0,
});

// List entities
const entities = await pk.listEntities({ role: "ai" });

// List attributions for an action
const attributions = await pk.listAttributions({ actionId });

Gotchas

  • CIDs must be deterministic. Two different callers hashing the same content must produce the same CID for the provenance graph to link correctly. Use IPFS CIDs (ipfs add --cid-version 1) or sha256:<hex> as a simple fallback for testing.
  • performedBy is the primary actor. Use inline attributions for additional contributors. The performedBy entity is automatically given an attribution with role: "primary" by the API.
  • Session IDs are not validated. The API accepts any string. Use UUIDs or another random ID generation strategy to ensure uniqueness across sessions.
  • Extensions are stored as-is. The API does not validate extension content beyond JSON serialization. Use the Zod schemas from @provenancekit/extensions in your application code before calling pk.file().