Developers
Certificates, passports and on-chain proofs are served by plain REST endpoints, most of them public, because a proof you cannot check yourself is not a proof. Scoped keys, signed webhooks and a typed SDK cover the rest.
https://api.sealtrust.io
The verification surface requires no account and no API key. Fetch a certificate, a passport, or the Merkle proof of a product and check our anchoring yourself on Base L2 (basescan.org). The proof is public and independently verifiable.
# Public endpoints — no API key required
curl https://api.sealtrust.io/certificate/{identifier}
# The passport as JSON-LD (Schema.org / GS1 vocabulary)
curl "https://api.sealtrust.io/passport/{identifier}?format=jsonld"
# The public Merkle proof — verify our anchoring yourself on Base L2
curl https://api.sealtrust.io/verify/merkle/{identifier}Everything below is live and unauthenticated (rate limits apply). Identifiers can be a uid_hash (0x + 64 hex), a token_id or a certificate number.
| Endpoint | What it does |
|---|---|
| GET/sdm/verify-url | Verify an NTAG 424 DNA scan (SDM): decrypts and validates the tag's single-use code. |
| GET/certificate/{identifier} | Public certificate of authenticity (by uid_hash, token_id or certificate number). |
| GET/certificate/{identifier}/download | The same certificate as a PDF. |
| GET/passport/{identifier} | Digital Product Passport, filtered by ESPR access tier. Add ?format=jsonld for JSON-LD (Schema.org/GS1). |
| GET/passport/{identifier}/vc | The passport as a tier-filtered SD-JWT-VC verifiable credential. |
| GET/passport/{identifier}/vc/verify | Verify the stored SD-JWT-VC against the brand's signing key. |
| GET/brand/{brand_id}/did.json | Brand DID Document (did:web): public signing keys as JsonWebKey2020. |
| GET/01/{gtin}/21/{serial} | GS1 Digital Link resolver: a GTIN + serial resolves to the product's passport. |
| GET/resolve/{identifier} | Universal resolver: product + certificate + passport + lifecycle events + media in one response. |
| GET/verify/merkle/{identifier} | Public Merkle anchor proof for an anchored product: recompute it yourself against Base L2. |
| GET/qr/product/{identifier} | QR code (PNG) pointing to the product's verification page. |
Write operations use API keys (prefix st_live_) sent as Authorization: Bearer. The full secret is shown once at creation; only its SHA-256 hash is stored. Each key is brand-scoped, quota-limited per day, rate-limited, and carries explicit scopes:
mint:batchBatch mint via /partner/mint/batchmint:singleSingle mint (upcoming)products:readRead productsproducts:statusProduct statustransfers:createCreate transfersPOST /partner/mint/batch accepts JSON (a list of items) or a CSV upload, enforces brand isolation and quotas, supports an Idempotency-Key header for safe retries, and returns a job you can poll.
curl -X POST https://api.sealtrust.io/partner/mint/batch \
-H "Authorization: Bearer st_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2027-0042" \
-d '[
{
"product_name": "Sneaker #001",
"brand_id": 1,
"category_id": 3,
"metadata_uri": "https://metadata.sealtrust.io/001.json"
}
]'
# → { "job_id": "abc123...", "status": "queued", "items_count": 1, "brand_id": 1 }
# Poll: GET /partner/mint/batch/status/{job_id} (max 500 items per batch)Subscribe a URL per brand and receive events as JSON POSTs. Every delivery is signed with your webhook secret: the X-Webhook-Signature header carries an HMAC-SHA256 (hex) of the payload serialized with sorted keys. Verify it in a few lines:
import hashlib
import hmac
import json
def verify_webhook(payload: dict, signature: str, secret: str) -> bool:
"""Verify the X-Webhook-Signature header (HMAC-SHA256, hex)."""
raw = json.dumps(payload, sort_keys=True, default=str)
expected = hmac.new(secret.encode(), raw.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
# FastAPI example
# sig = request.headers["X-Webhook-Signature"]
# assert verify_webhook(await request.json(), sig, WEBHOOK_SECRET)product.mintedA product NFT has been minted on-chainproduct.transferredProduct ownership has been transferredproduct.burnedA product NFT has been burnedproduct.status_changedProduct status has changedbatch.completedA batch mint job completed successfullybatch.failedA batch mint job failedcertificate.issuedA certificate of authenticity was issued@sealtrust/sdk is a type-safe client with native fetch and zero dependencies (Node.js ≥ 18). POST and PUT requests automatically carry an X-Idempotency-Key header; you can provide your own to retry safely.
import { SealTrustClient } from "@sealtrust/sdk";
const sealtrust = new SealTrustClient({
apiKey: "st_live_...",
baseUrl: "https://api.sealtrust.io", // optional, this is the default
});
// Verify a product by UID hash
const result = await sealtrust.verify.product("0xabc123...def");
console.log(result.valid); // true
console.log(result.message); // "Authentic product: signature and UID validated."
// Mint a batch, then poll the job
const job = await sealtrust.products.mint([
{ product_name: "Sneaker #001", brand_id: 1, category_id: 3, metadata_uri: "https://metadata.sealtrust.io/001.json" },
]);
const status = await sealtrust.products.getBatchStatus(job.job_id);
console.log(status.status); // "queued" | "started" | "finished" | "failed"
// Subscribe to webhooks
await sealtrust.webhooks.create({
url: "https://example.com/webhooks/sealtrust",
events: ["product.minted", "product.transferred"],
secret: "whsec_...",
});Each product identity is addressable through the GS1 Digital Link syntax the ESPR ecosystem converges on: /01/{gtin}/21/{serial} resolves a GTIN + serial straight to the item's Digital Product Passport, so the same carrier works for retailers, customs and recyclers without bespoke integrations.
# One GS1 Digital Link per item — GTIN + serial → passport
curl https://api.sealtrust.io/01/{gtin}/21/{serial}
# The same identity also resolves certificates and events
curl https://api.sealtrust.io/resolve/{identifier}@sealtrust-io/mcp-server exposes the public verification surface as a Model Context Protocol (MCP) server: six read-only tools that any MCP client (Claude Desktop, Claude Code and others) can call to verify a product, read its Digital Product Passport and check the proofs behind it. It runs locally over stdio, needs no account and no API key, and only ever reaches the public endpoints listed above.
{
"mcpServers": {
"sealtrust": {
"command": "npx",
"args": ["-y", "@sealtrust-io/mcp-server"]
}
}
}# Claude Code
claude mcp add sealtrust -- npx -y @sealtrust-io/mcp-server
# Optional: point it at another environment (default: https://api.sealtrust.io)
claude mcp add sealtrust --env SEALTRUST_API_URL=https://api.sealtrust.io -- npx -y @sealtrust-io/mcp-serververify_productAuthenticity status of a product (authentic, revoked or unknown) plus public product info.get_passportThe published Digital Product Passport, public tier only (JSON or JSON-LD).get_passport_proofProof bundle: SHA-256 data hash, IPFS copy, Base L2 anchor, SD-JWT-VC status.get_certificateThe public certificate of authenticity (status, dates, issuer).resolve_gs1Resolve a GS1 Digital Link (GTIN + serial) to the item's passport.verify_credentialVerify the passport's SD-JWT-VC against the brand's did:web signing key.Read-only by design: the server can only GET public data. Nothing it does can mint, transfer or modify a product.
Want a sandbox key or an integration review with our team?
Talk to us