Implementation examples

Worked end-to-end TypeScript examples for IXO MultiClient SDK and IXO Matrix Client SDK.

These examples follow the canonical patterns from the @ixo/impactxclient-sdk and @ixo/matrixclient-sdk READMEs. Use them as starting templates — exact field shapes are generated from the chain proto definitions.

For the step-by-step explanation behind each pattern, read Developer workflows first.

Setup

npm install @ixo/impactxclient-sdk @ixo/matrixclient-sdk
import {
  ixo,
  createQueryClient,
  createSigningClient,
} from "@ixo/impactxclient-sdk";

import {
  createMatrixApiClient,
  createMatrixRoomBotClient,
  createMatrixStateBotClient,
  createMatrixClaimBotClient,
} from "@ixo/matrixclient-sdk";

const RPC_ENDPOINT = "https://rpc.ixo.world";
const HOME_SERVER_URL = "https://devmx.ixo.earth";

const queryClient = await createQueryClient(RPC_ENDPOINT);
const signingClient = await createSigningClient(RPC_ENDPOINT, offlineSigner);

Digital twin: create an entity

const createEntity = async (signerAddress: string) => {
  const message = {
    typeUrl: "/ixo.entity.v1beta1.MsgCreateEntity",
    value: ixo.entity.v1beta1.MsgCreateEntity.fromPartial({
      ownerAddress: signerAddress,
      entityType: "asset",
      context: [
        ixo.iid.v1beta1.Context.fromPartial({
          key: "class",
          val: "did:ixo:entity:protocol123",
        }),
      ],
      verification: [],
      controller: [],
      service: [],
      linkedResource: [],
      accordedRight: [],
      linkedEntity: [],
      linkedClaim: [],
    }),
  };

  return signingClient.signAndBroadcast(signerAddress, [message], "auto");
};

const entities = await queryClient.ixo.entity.v1beta1.entityList();

Source an entity room (IXO Matrix)

Each entity has an associated encrypted Matrix room — its data room. Source it with the room bot, which creates the room if needed and invites the caller.

const matrixRoomBotClient = createMatrixRoomBotClient({
  homeServerUrl: HOME_SERVER_URL,
  botUrl: "https://devmx.ixo.earth/bots/room",
  accessToken: matrixAccessToken,
});

const sourceRoomResponse = await matrixRoomBotClient.room.v1beta1.sourceRoomAndJoin(
  "did:ixo:entity:abc...xyz",
  ucanToken,
);

const roomId = sourceRoomResponse.roomId;

The ucanToken carries the caller's capability to act on the entity. The optional accessToken is the user's Matrix client-server access token, used only for the local join — never sent to the bot server. See Matrix authentication.

Upload telemetry into the data room

const matrixApiClient = createMatrixApiClient({
  homeServerUrl: HOME_SERVER_URL,
  accessToken: matrixAccessToken,
});

const upload = await matrixApiClient.media.v1beta1.upload(
  "telemetry-2025-04.json",
  "application/json",
  telemetryFile,
);

const mxcUri = upload.content_uri;

Read or set room state via the State Bot

Use the State Bot to attach structured profile or settings data to an entity room.

const matrixStateBotClient = createMatrixStateBotClient({
  homeServerUrl: HOME_SERVER_URL,
  botUrl: "https://devmx.ixo.earth/bots/state",
  accessToken: matrixAccessToken,
});

const stateData = await matrixStateBotClient.state.v1beta1.queryState(
  roomId,
  "impactsX",
  "profile",
  ucanToken,
);

await matrixStateBotClient.state.v1beta1.setState(
  roomId,
  "impactsX",
  "profile",
  JSON.stringify({ displayName: "Solar Farm Alpha", capacityKw: 5000 }),
  ucanToken,
);

Claim flow: submit, evaluate, mint

End-to-end flow for a digital MRV outcome:

const collectionId = "12";
const adminAddress = "ixo1...";
const oracleAddress = "ixo1oracle...";
const tokenContractAddress = "ixo1contract...";

// 1. Submit a claim referencing evidence stored in the data room
const submitClaimMsg = {
  typeUrl: "/ixo.claims.v1beta1.MsgSubmitClaim",
  value: ixo.claims.v1beta1.MsgSubmitClaim.fromPartial({
    creator: signerAddress,
    claimId: "bafkreih...",
    collectionId,
    adminAddress,
  }),
};

await signingClient.signAndBroadcast(signerAddress, [submitClaimMsg], "auto");

// 2. Optional Claim Bot helpers (off-chain co-ordination)
const matrixClaimBotClient = createMatrixClaimBotClient({
  homeServerUrl: HOME_SERVER_URL,
  botUrl: "https://devmx.ixo.earth/bots/claim",
  accessToken: matrixAccessToken,
});

const claimRecord = await matrixClaimBotClient.claim.v1beta1.queryClaim(
  collectionId,
  "bafkreih...",
  ucanToken,
);

// 3. Evaluate after the Agentic Oracle has produced a verification
const evaluateMsg = {
  typeUrl: "/ixo.claims.v1beta1.MsgEvaluateClaim",
  value: ixo.claims.v1beta1.MsgEvaluateClaim.fromPartial({
    creator: signerAddress,
    claimId: "bafkreih...",
    collectionId,
    oracle: oracleAddress,
    adminAddress,
    status: 1, // Approved
    reason: 0,
    verificationProof: "bafkrei-proof-cid",
    amount: [],
  }),
};

await signingClient.signAndBroadcast(signerAddress, [evaluateMsg], "auto");

// 4. Mint outcome tokens against the approved claim
const mintMsg = {
  typeUrl: "/ixo.token.v1beta1.MsgMintToken",
  value: ixo.token.v1beta1.MsgMintToken.fromPartial({
    minter: signerAddress,
    contractAddress: tokenContractAddress,
    owner: recipientAddress,
    mintBatch: [
      ixo.token.v1beta1.MintBatch.fromPartial({
        name: "CARBON",
        index: "bafkreih...",
        amount: "3600",
        collection: "did:ixo:entity:collection456",
        tokenData: [],
      }),
    ],
  }),
};

await signingClient.signAndBroadcast(signerAddress, [mintMsg], "auto");

Agentic Oracle integration

The off-chain oracle service is built with the Agentic Oracles ADK and Personal Agent ADK. The on-chain side uses @ixo/impactxclient-sdk to record MsgEvaluateClaim. Wire the two together with whatever HTTP, MCP, or messaging layer your service exposes.

The IXO Oracles ecosystem is private. To request access, see Build agentic oracles.

Next steps