On this page
Build with familiar tools and explicit interfaces.

Register, fund, send, verify

Choose a public principal flow or a human payment journey before selecting a client. agent.register registers an agent with the daemon; lx_register creates the public principal binding. They serve different workflows. Fund through an authenticated faucet claim and submit signed canonical activities with a scoped LayerX key.

Rust

Use the typed RpcClient for registration, funding requests, and canonical activity submission. Sign the binding digest returned by register::binding before calling register. Funding requires the identity session authorization on the gateway request.

Registration binding

use layerx_sdk::register::{binding, TENANT};
use layerx_sdk::rpc::RpcClient;

fn register(rpc: &RpcClient, public_key: [u8; 32], signature: &[u8; 64])
    -> Result<layerx_sdk::register::Registration, layerx_sdk::rpc::RpcError>
{
    let _digest = binding(TENANT, &public_key);
    rpc.register(public_key, signature)
}

Authenticated funding

use layerx_sdk::rpc::{FaucetGrant, RpcClient, RpcError};

fn request_funds(rpc: &RpcClient, did: &str, public_key: &[u8; 32])
    -> Result<FaucetGrant, RpcError>
{
    rpc.request_funds(did, public_key)
}

Client and payment example

use layerx_sdk::rpc::{Commitment, RpcClient, RpcError};

fn send(rpc: &RpcClient, canonical: &[u8]) -> Result<serde_json::Value, RpcError> {
    rpc.send_activity(canonical, Commitment::Executed)
}

Client and payment example

use layerx_sdk::production::SecretBytes;
use layerx_sdk::programs::LayerXKeyCredential;
use layerx_sdk::rpc::RpcClient;

fn connect(endpoint: &str, key_id: &str, secret: &[u8]) -> Result<RpcClient, layerx_sdk::rpc::RpcError> {
    let secret = SecretBytes::new(secret).map_err(|_| layerx_sdk::rpc::RpcError::InvalidRequest)?;
    let credential = LayerXKeyCredential::new(key_id, secret).map_err(layerx_sdk::rpc::RpcError::Configuration)?;
    RpcClient::connect(endpoint, Some(credential))
}

Local receipt verification

use layerx_proof::receipt::AuthorizedBatch;
use layerx_sdk::production::verify_receipt;

fn verify(
    receipt: &[u8],
    batch_id: [u8; 32],
    asset: [u8; 32],
    previous_state_root: [u8; 32],
    resulting_state_root: [u8; 32],
    sequencer_public_key: [u8; 32],
) -> Result<layerx_proof::receipt::VerifiedReceipt, layerx_sdk::production::ReceiptVerificationFailure> {
    let authorised = AuthorizedBatch::new(
        batch_id,
        asset,
        previous_state_root,
        resulting_state_root,
        sequencer_public_key,
    );
    verify_receipt(receipt, &authorised)
}

TypeScript

Use JsonRpcClient for native submission and reads. Use public JSON-RPC for registration and faucet methods; ProductionClient provides human and agent operation interfaces with an appropriate transport.

Client and payment example

import { JsonRpcClient, LayerXKeyCredential, SecretBytes } from "@sidiora/layerx-sdk";
import type { Commitment } from "@sidiora/layerx-sdk";

export function openRpc(endpoint: string, keyId: string, secret: Uint8Array): JsonRpcClient {
  return new JsonRpcClient(endpoint, new LayerXKeyCredential(keyId, new SecretBytes(secret)));
}

export function send(
  rpc: JsonRpcClient,
  canonical: Uint8Array,
  commitment: Commitment = "executed",
): Promise<Record<string, unknown>> {
  return rpc.sendActivity(canonical, commitment);
}

Client and payment example

import { ProductionClient, SecretBytes, idempotencyKey } from "@sidiora/layerx-sdk";

export async function pay(
  layerx: ProductionClient,
  source: string,
  destination: string,
  money: { amount: string; currency: string },
  paymentKey: string,
) {
  const quote = await layerx.human("move.quote", { source, destination, money });
  return layerx.human("move.commit", { quote_id: quote.quote_id }, { idempotencyKey: idempotencyKey(paymentKey) });
}

Local receipt verification

import { verifyReceipt } from "@sidiora/layerx-sdk";
import type { AuthorizedReceiptBatch, ReceiptVerification } from "@sidiora/layerx-sdk";

export function verify(
  canonicalReceipt: Uint8Array,
  authorized: AuthorizedReceiptBatch,
): Promise<ReceiptVerification> {
  return verifyReceipt(canonicalReceipt, authorized);
}

Python

PaymentRpc.call sends public method names directly. Registration uses lowercase hexadecimal key and signature values. Faucet calls use a Bearer identity session; signed activity calls use the LayerX-Key header.

Public registration

from layerx_sdk import PaymentRpc

def register(rpc: PaymentRpc, signer_public_key: str, registration_signature: str) -> dict:
    return rpc.call("lx_register", [signer_public_key, registration_signature])

Authenticated funding

from layerx_sdk import PaymentRpc

def request_funds(endpoint: str, session_token: str, did: str, signer_public_key: str) -> dict:
    rpc = PaymentRpc(endpoint, {"Authorization": f"Bearer {session_token}"})
    return rpc.call("lx_requestFunds", [did, signer_public_key])

Client and payment example

from layerx_sdk import PaymentRpc

def send(rpc: PaymentRpc, canonical_hex: str, commitment: str = "executed") -> dict:
    return rpc.send(canonical_hex, commitment)

Client and payment example

from layerx_sdk import IdempotencyKey, ProductionClient

def pay(layerx: ProductionClient, source, destination, money, payment_key):
    quote = layerx.human("move.quote", {"source": source, "destination": destination, "money": money})
    return layerx.human("move.commit", {"quote_id": quote["quote_id"]}, idempotency_key=IdempotencyKey(payment_key))

Local receipt verification

from layerx_sdk import (
    AuthorizedReceiptBatch,
    LocalSignatureVerifier,
    ReceiptVerification,
    verify_receipt,
)

def verify(
    canonical_receipt: bytes,
    authorized_batch: AuthorizedReceiptBatch,
    signatures: LocalSignatureVerifier,
) -> ReceiptVerification:
    return verify_receipt(canonical_receipt, authorized_batch, signatures)

Go

Use NewHumanHTTPTransport and Client.Human for a quote followed by an idempotent commit. Agent catalog operations require an agent-plane transport.

Client and payment example

package main

import (
	"context"
	"net/http"

	layerx "github.com/Sidiora-Labs/LayerX-Network/platform/sdk/go"
)

type Money struct {
	Amount   string `json:"amount"`
	Currency string `json:"currency"`
}

type MoveQuoteRequest struct {
	Source      string `json:"source"`
	Destination string `json:"destination"`
	Money       Money  `json:"money"`
}

type MoveQuote struct {
	QuoteID string `json:"quote_id"`
}

type MoveCommitRequest struct {
	QuoteID string `json:"quote_id"`
}

type Journey struct {
	JourneyID string `json:"journey_id"`
	State     string `json:"state"`
}

func pay(ctx context.Context, apiURL, apiToken, source, destination, paymentKey string, money Money) (Journey, error) {
	authorize := func(request *http.Request) error {
		request.Header.Set("Authorization", "Bearer "+apiToken)
		return nil
	}
	transport, err := layerx.NewHumanHTTPTransport(apiURL, nil, authorize)
	if err != nil {
		return Journey{}, err
	}
	client, err := layerx.NewClient(transport, nil)
	if err != nil {
		return Journey{}, err
	}
	key, err := layerx.NewIdempotencyKey(paymentKey)
	if err != nil {
		return Journey{}, err
	}
	var quote MoveQuote
	if err := client.Human(ctx, layerx.HumanOperationMoveQuote, MoveQuoteRequest{Source: source, Destination: destination, Money: money}, &quote, layerx.CallOptions{}); err != nil {
		return Journey{}, err
	}
	var journey Journey
	if err := client.Human(ctx, layerx.HumanOperationMoveCommit, MoveCommitRequest{QuoteID: quote.QuoteID}, &journey, layerx.CallOptions{IdempotencyKey: key}); err != nil {
		return Journey{}, err
	}
	return journey, nil
}

Local receipt verification

package main

import layerx "github.com/Sidiora-Labs/LayerX-Network/platform/sdk/go"

func verify(canonicalReceipt []byte, authorized layerx.AuthorizedBatch) (layerx.VerifiedReceipt, error) {
	return layerx.VerifyReceipt(canonicalReceipt, authorized)
}

JVM

Configure HttpProductionTransport with the human origin and the agent origin. Human payments use ProductionClient.human and typed options.

Client and payment example

import com.fasterxml.jackson.databind.JsonNode;
import com.sidiora.layerx.sdk.HttpProductionTransport;
import com.sidiora.layerx.sdk.IdempotencyKey;
import com.sidiora.layerx.sdk.ProductionClient;
import com.sidiora.layerx.sdk.SecretBytes;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;

public final class Pay {
    public static JsonNode pay(
        String apiUrl,
        String apiToken,
        String source,
        String destination,
        Map<String, String> money,
        String paymentKey
    ) {
        var credential = new HttpProductionTransport.BearerCredential(
            new SecretBytes(apiToken.getBytes(StandardCharsets.UTF_8)));
        var layerx = new ProductionClient(
            HttpProductionTransport.create(URI.create(apiUrl), URI.create(apiUrl), credential));
        var quote = layerx.human(
            "move.quote",
            Map.of("source", source, "destination", destination, "money", money),
            JsonNode.class,
            ProductionClient.Options.none()).toCompletableFuture().join();
        return layerx.human(
            "move.commit",
            Map.of("quote_id", quote.path("quote_id").asText()),
            JsonNode.class,
            ProductionClient.Options.idempotent(new IdempotencyKey(paymentKey)))
            .toCompletableFuture().join();
    }

    private Pay() {}
}

Local receipt verification

import com.sidiora.layerx.sdk.verify.LocalVerifier;
import com.sidiora.layerx.sdk.verify.LocalVerifier.AuthorizedReceiptBatch;
import com.sidiora.layerx.sdk.verify.LocalVerifier.ReceiptVerification;

public final class Verify {
    public static ReceiptVerification verify(byte[] canonicalReceipt, AuthorizedReceiptBatch authorized) {
        return LocalVerifier.verifyReceipt(canonicalReceipt, authorized);
    }

    private Verify() {}
}

Swift

Use PlatformClient with HumanHTTPTransport for a payment quote and commit. Access tokens and idempotency keys use dedicated types.

Client and payment example

import Foundation
import LayerXSDK

func pay(
    serviceURL: URL,
    apiToken: String,
    source: String,
    destination: String,
    money: JSONValue,
    paymentKey: IdempotencyKey
) async throws -> JSONValue {
    let token = try AccessToken(Data(apiToken.utf8))
    let layerx = PlatformClient(transport: try HumanHTTPTransport(baseURL: serviceURL, accessToken: token))
    let quote = try await layerx.humanMoveQuote(.object([
        "source": .string(source),
        "destination": .string(destination),
        "money": money,
    ]))
    guard let quoteID = quote.objectValue?["quote_id"]?.stringValue else {
        throw PlatformSDKError(code: .invalidArgument, retry: .never)
    }
    return try await layerx.humanMoveCommit(.object(["quote_id": .string(quoteID)]), idempotencyKey: paymentKey)
}

Local receipt verification

import Foundation
import LayerXSDK

func verify(canonicalReceipt: Data, authorized: AuthorizedReceiptBatch) async throws -> ReceiptVerification {
    try await LocalVerifier.verifyReceipt(canonicalReceipt, authorized: authorized)
}

.NET

Use PlatformClient with HumanHttpTransport, AccessToken, and IdempotencyKey for the human payment flow.

Client and payment example

using System.Text;
using LayerX.Sdk;

static async Task<JsonValue> Pay(
    Uri apiUrl,
    string apiToken,
    string source,
    string destination,
    JsonValue money,
    string paymentKey)
{
    using var token = new AccessToken(Encoding.UTF8.GetBytes(apiToken));
    var layerx = new PlatformClient(new HumanHttpTransport(apiUrl, accessToken: token));
    var quote = await layerx.HumanMoveQuoteAsync(JsonValue.Object(new Dictionary<string, JsonValue>
    {
        ["source"] = JsonValue.String(source),
        ["destination"] = JsonValue.String(destination),
        ["money"] = money,
    }));
    IReadOnlyDictionary<string, JsonValue> fields = quote is JsonValue.ObjectValue record
        ? record.Value
        : new Dictionary<string, JsonValue>();
    string quoteId = fields.TryGetValue("quote_id", out var found) && found is JsonValue.StringValue text
        ? text.Value
        : string.Empty;
    return await layerx.HumanMoveCommitAsync(
        JsonValue.Object(new Dictionary<string, JsonValue> { ["quote_id"] = JsonValue.String(quoteId) }),
        new IdempotencyKey(paymentKey));
}

Local receipt verification

using LayerX.Sdk;

static ValueTask<ReceiptVerification> Verify(
    ReadOnlyMemory<byte> canonicalReceipt,
    AuthorizedReceiptBatch authorized) =>
    LocalVerifier.VerifyReceiptAsync(canonicalReceipt, authorized);

Trust the batch authorization

Every local receipt verifier takes five independently trusted 32-byte batch facts: batch ID, asset identity, previous state root, resulting state root, and sequencer public key. Supplying these from the same untrusted response as the receipt does not establish independent trust. Python also takes a caller-provided signature verifier implementing Ed25519 and recoverable secp256k1 verification.

Success-only verification refuses a nonzero result code. Use the outcome-verification entry point when inspecting a refused execution. Treat executed, batched, and finalised as distinct commitments.

Programs terminal evidence

A Programs CALL receipt commits to one runtime terminal. Verify the receipt outcome first, then hash the terminal payload and call graph against their receipt commitments. Terminal kinds distinguish success (1), failure (2), and resource exhaustion (3).

Encoding 4 carries applied transfer legs. Each canonical leaf is 115 bytes; the verifier bounds the list at 256 legs, checks canonical nonzero fields and the applied-legs digest, and reconstructs the transfer Merkle root. Truncated payloads, trailing bytes, mutated amounts, and mismatched roots are refused.

SDKTerminal verification entry point
Pythondecode_and_verify_program_terminal
TypeScriptdecodeAndVerifyProgramTerminal
GoverifyProgramTerminal
JVMProgramsClient.verifyTerminal
SwiftverifyTerminal
.NETProgramsClient.VerifyTerminal

Transfer evidence reports reconstructed when the terminal locally rebuilds the transfer root. Historical receipts can report recorded_terminal_root_not_locally_reconstructable; preserve that distinction in application output. Protocol 3 occupancy verification proves payer DID account derivations against the settlement and refuses a paid occupancy charge without a proven payment account.

Continue with XDK API families, Programs, and MCP tools.

Paxeer X · System documentationBack to top ↑

Ask Paxeer X Docs

Answers from the documentation.

What would you like to know?

Ask a question, find a guide, or get help with your next step.

Enter to send · Shift+Enter for a new line