SDK quickstarts
Register a principal, fund an account, send a payment, and independently verify receipts with the Paxeer X SDKs.
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}, "e, 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.
| SDK | Terminal verification entry point |
|---|---|
| Python | decode_and_verify_program_terminal |
| TypeScript | decodeAndVerifyProgramTerminal |
| Go | verifyProgramTerminal |
| JVM | ProgramsClient.verifyTerminal |
| Swift | verifyTerminal |
| .NET | ProgramsClient.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.