Skip to main content

Integrate with XNDR Network

Three workflows for building on indexed ALX Blocks. The snippets use client.js and client.py from Reference Implementations.

Decide how the interface shows a missing, unavailable, or unresolved relationship before following these workflows. Getting Started covers the base URL, the optional API key, and the first request. Requests use scope=default unless another scope is passed.

Workflow 1: Find and Retrieve a Block​

Search is exact: a Block hash fills data.block; a content hash returns one page of matches in data.contentMatches. Use a chosen candidate's blockHash as the stable identifier in links and application state. Follow page.nextCursor to offer more content matches.

import { xndrGet } from './client.js';

const query = process.env.XNDR_BLOCK_HASH; // a complete 0x-prefixed Block or content hash
const search = await xndrGet('/search', { q: query });
const snapshot = {
scope: search.scope.id,
scopeVersion: search.scope.version,
atRevision: search.scope.indexRevision,
};

if (search.data.block) {
const selectedHash = search.data.block.block.blockHash;
const { data, scope } = await xndrGet(`/blocks/${selectedHash}`, snapshot);

console.log(data.block.blockHash, data.block.contentHash, scope.id);
} else {
// Let the user choose a Block; do not silently select the first content match.
const candidates = (search.data.contentMatches ?? []).map(({ block }) => block.blockHash);
console.log({ candidates, nextCursor: search.page.nextCursor, ...snapshot });
}

An empty candidate list means no match in the selected scope. If nextCursor is set, request another search page with the same query and snapshot when the user asks for more. Show the chosen Block's hashes, observation times, scope, and revision; use the parent lookup to show declared-parent availability.

Workflow 2: Navigate Block Lineage​

Retrieve a small relationship page first, then expand on request. Parents come from the protocol-declared parentHashes; children are reverse-index observations limited to the selected scope.

import { xndrGet } from './client.js';

const hash = process.env.XNDR_BLOCK_HASH;
const focus = await xndrGet(`/blocks/${hash}`);
// Keep every related request on the Block view's scope and snapshot.
const snapshot = {
scope: focus.scope.id,
scopeVersion: focus.scope.version,
atRevision: focus.scope.indexRevision,
};

const parents = await xndrGet(`/blocks/${hash}/parents`, { ...snapshot, limit: 10 });
const children = await xndrGet(`/blocks/${hash}/children`, { ...snapshot, limit: 10 });

const view = {
block: focus.data.block,
declaredParents: focus.data.block.parentHashes,
parents: parents.data,
children: children.data,
parentNextCursor: parents.page.nextCursor,
childNextCursor: children.page.nextCursor,
indexedChildCount: focus.data.indexedChildCount,
scopeId: focus.scope.id,
scopeVersion: focus.scope.version,
indexRevision: focus.scope.indexRevision,
};

For a bounded ancestry view, use the neighborhood helper. Depth 2 and maxNodes 200 limit that request. The ancestry is complete only when page.nextCursor is null and data.budgetTruncated is false. Show scope.completeness, unindexed declared parents, page.nextCursor, and data.budgetTruncated with the result, and send the same scope and revision on any further page.

Read available on each returned parent. One parent page can omit later declared parents while parentNextCursor is set. The next workflow separates an empty parentHashes list from a declared parent the scope has not indexed. Neither statement claims that an unobserved Block does not exist.

Workflow 3: Separate Identity from Observations​

missingParents lists declared parents the selected scope has not indexed. An empty parentHashes list means the Block declared no parents. Canonical fields stay under data.block. Observation times, sources, and verification facets belong to the selected scope.

export function parentStatement(block, verification) {
if (block.parentHashes.length === 0) return 'No parents declared';
if ((verification?.missingParents ?? []).length > 0) {
return 'Declared parent not indexed in this scope';
}
return null;
}

Show that statement beside the Block. Error codes, cursor restarts, and rate limits are in API Overview. A full pagination helper is in Reference Implementations.