Verify — Public Verification & Compare
Publicly verify a proof root with no authentication, or compare your balance against a counterparty's.
This page documents two verification operations: public verification (anyone with a proof root can verify) and compare (resolving balance disputes between parties).
Public Verification — Verify a Proof Root
Use this endpoint to verify a proof root. No authentication required — the proof root itself is the access key. Share it with counterparties, auditors, or autonomous systems so they can independently confirm a receipt or checkpoint.
GET https://balance-api.pacspace.io/api/v1/verify/:proofRoot
Authentication
None. This is a public endpoint. The proof root is a 256-bit content fingerprint — it cannot be guessed or enumerated. Possession of the proof root grants read-only access to that specific verification result.
Three Trust Levels
- Human-readable summary — The response includes
verified,summary(record count, net change), andrecordsfor spot-checking. - Math verification — Recompute the proof root from the records using the proof specification and confirm it matches.
- Public ledger — The
verification.publicLedgerUrllinks to the permanent record on the verification layer. Anyone can confirm the proof exists independently of PacSpace.
Example Request
curl https://balance-api.pacspace.io/api/v1/verify/0x7f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f
Example Response
{
"success": true,
"data": {
"verified": true,
"proofRoot": "0x7f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f",
"recordedAt": "2026-02-18T14:32:00.000Z",
"summary": {
"recordCount": 42,
"netChange": -6300,
"startingBalance": 0,
"endingBalance": -6300,
"firstRecordAt": "2026-02-01T10:00:00.000Z",
"lastRecordAt": "2026-02-18T14:30:00.000Z"
},
"records": [
{
"amount": -150,
"reason": "api_call",
"referenceId": "inv_2026_02_item_001",
"status": "verified",
"time": "2026-02-01T10:00:00.000Z"
}
],
"verification": {
"reference": "0xabc123...",
"publicLedgerUrl": "https://hashscan.io/mainnet/transaction/0xabc123...",
"note": "This proof exists on a public verification layer that neither party controls."
},
"message": "This proof was permanently recorded and is independently verifiable."
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
verified | boolean | Whether the proof was found and permanently recorded |
proofRoot | string | The proof root that was verified |
recordedAt | string | When the proof was permanently recorded |
summary | object | Aggregate: recordCount, netChange, startingBalance, endingBalance, firstRecordAt, lastRecordAt |
records | array | Individual records: amount, reason, status, time (and optionally referenceId, itemFingerprint) |
verification | object | reference (ledger transaction ID), publicLedgerUrl, note |
Privacy
No party-identifying data is returned. Tenant ID, customer ID, and other business identifiers stay private. The response contains only the proof root, timestamps, amounts, reasons, and verification metadata — enough to confirm the math without exposing who the parties are.
Agent-Ready
This endpoint is designed for machine-speed verification by autonomous systems. Poll it to confirm a proof root exists, parse the records array for reconciliation, or follow publicLedgerUrl for full independent verification. No API keys, no sessions, no rate limits beyond standard HTTP.
Compare — Resolve Balance Disputes
Use this endpoint when two parties disagree on a balance. PacSpace independently derives the correct balance from verified deltas and reports which side (if either) has a discrepancy.
POST https://balance-api.pacspace.io/api/v1/balance/compare
Headers
| Header | Required | Description |
|---|---|---|
X-Api-Key | Yes | Your PacSpace API key |
Content-Type | Yes | Must be application/json |
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | Unique identifier for the customer account |
yourBalance | number | Yes | The balance your system currently shows. Must be a finite number between -1,000,000,000 and 1,000,000,000. |
theirBalance | number | Yes | The balance the counterparty claims. Must be a finite number between -1,000,000,000 and 1,000,000,000. |
startingBalance | number | Yes | Agreed-upon starting balance for the comparison window |
startingCheckpoint | string | No | Checkpoint to begin the comparison from |
startingCheckpointType | string | No | Type of checkpoint: itemsRoot or anchorId |
Examples
SDK (TypeScript)
import { PacSpace } from '@pacspace-io/sdk';
const pac = new PacSpace({ apiKey: process.env.PACSPACE_API_KEY });
const report = await pac.balance.compare('cust_12345', {
yours: 48500,
theirs: 49000,
}, {
startingBalance: 50000,
startingCheckpoint: '0xdef456...',
startingCheckpointType: 'itemsRoot',
});
if (report.matchesYours) {
console.log('Your balance is correct');
} else if (report.discrepancyReport) {
console.log(`Discrepancy: ${report.discrepancyReport.amount}`);
console.log(report.discrepancyReport.resolution);
}
cURL
curl -X POST https://balance-api.pacspace.io/api/v1/balance/compare \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cust_12345",
"yourBalance": 48500,
"theirBalance": 49000,
"startingBalance": 50000,
"startingCheckpoint": "0xdef456...",
"startingCheckpointType": "itemsRoot"
}'
JavaScript (Node.js)
const response = await fetch(
"https://balance-api.pacspace.io/api/v1/balance/compare",
{
method: "POST",
headers: {
"X-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
customerId: "cust_12345",
yourBalance: 48500,
theirBalance: 49000,
startingBalance: 50000,
startingCheckpoint: "0xdef456...",
startingCheckpointType: "itemsRoot",
}),
}
);
const result = await response.json();
console.log(result);
Python
import requests
response = requests.post(
"https://balance-api.pacspace.io/api/v1/balance/compare",
headers={
"X-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"customerId": "cust_12345",
"yourBalance": 48500,
"theirBalance": 49000,
"startingBalance": 50000,
"startingCheckpoint": "0xdef456...",
"startingCheckpointType": "itemsRoot",
},
)
result = response.json()
print(result)
Response
{
"success": true,
"data": {
"customerId": "cust_12345",
"yourBalance": 48500,
"theirBalance": 49000,
"neutralBalance": 48500,
"matchesYours": true,
"matchesTheirs": false,
"deltasVerified": 127,
"discrepancyReport": {
"amount": 500,
"resolution": "Your balance is correct. The counterparty is overstated by 500.",
"divergentParty": "theirs",
"windowsToReview": [
{
"window": "2026-02",
"netDelta": -1500,
"deltasCount": 15,
"timeRange": "2026-02-01T00:00:00Z to 2026-02-11T23:59:59Z"
}
],
"recommendation": "Share this report with the counterparty. The discrepancy originates in the February 2026 window."
},
"proof": {
"itemsRoot": "0xabc789...",
"latestCheckpoint": "0xabc789...",
"windowSummaries": [
{
"window": "2026-02",
"deltasCount": 15,
"netDelta": -1500,
"firstBlockTimestamp": "2026-02-01T10:15:00Z",
"lastBlockTimestamp": "2026-02-11T22:45:00Z"
}
]
}
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
customerId | string | The customer account that was compared |
yourBalance | number | The balance you submitted |
theirBalance | number | The balance the counterparty submitted |
neutralBalance | number | The independently derived balance from verified deltas |
matchesYours | boolean | Whether your submitted balance matches the neutral balance |
matchesTheirs | boolean | Whether the counterparty's balance matches the neutral balance |
deltasVerified | number | Number of verified deltas included in the comparison |
discrepancyReport | object | null | Detailed breakdown when a mismatch is found (see below) |
proof | object | Proof data covering the comparison (see below) |
Discrepancy Report
Returns null when all three balances match (yours, theirs, and the neutral truth). Otherwise, contains a detailed breakdown:
| Field | Type | Description |
|---|---|---|
amount | number | The absolute difference between the two balances |
resolution | string | Human-readable explanation of the finding |
divergentParty | string | Which side is incorrect: "yours", "theirs", or "both" |
windowsToReview | array | Time windows where the discrepancy originated |
relevantWindows | array | Top 5 most impactful windows for investigation, sorted by absolute net delta |
recommendation | string | Suggested next steps to resolve the discrepancy |
When both parties agree with each other but both differ from the neutral truth, divergentParty is "both" with an explanation that both sides have drifted from the verified record.
Window to Review Object
| Field | Type | Description |
|---|---|---|
window | string | Time window where the mismatch occurred |
netDelta | number | The net change derived from verified deltas |
deltasCount | number | Number of deltas in the window |
timeRange | string | null | Date range of the window (null if no timestamps available) |
Proof Object
| Field | Type | Description |
|---|---|---|
itemsRoot | string | Proof root covering all deltas used in the comparison |
latestCheckpoint | string | null | Latest checkpoint used in the comparison |
windowSummaries | array | Window-by-window summary of verified deltas |
How It Works
-
You submit both balances — your view and the counterparty's view, along with an agreed starting point.
-
PacSpace derives the truth — Using only verified deltas, PacSpace computes the neutral balance independently of either party's records.
-
You get a verdict — The response tells you exactly who matches, who diverges, when the discrepancy started, and what to do about it.
Tip: If both
matchesYoursandmatchesTheirsaretrue, the balances agree and nodiscrepancyReportis returned. If both arefalse, both parties have drifted from the verified record.