Integration
A minimal, honest integration guide for developers wiring Tessaliq into their SaaS. Every snippet below reflects the actual staging API as of 2026-04-17 — no aspirational endpoints, no SDK features that are not shipped.
Tessaliq is in Alpha / early access. There is no public npm publish today and no self-serve signup form. To get a real API key and SDK access, start a conversation via the contact form. The sections below let you see exactly what you would be signing up for before reaching out.
1. Install
Public npm distribution is not enabled yet. Options for a pilot today:
- Clone + workspace link — the open-core repo
Tessaliq/tessaliq-opencontains@tessaliq/sdk-webunder MIT. Clone it, link via pnpm workspace, build, use locally. - Request a pre-built tarball — reach out via the contact form, get a signed tarball for your project.
A public npm publish is planned once a first pilot is running and the package contract is stable. Until then, versioning is tied to the Git tag.
2. Initialize the SDK
import { Tessaliq } from '@tessaliq/sdk-web';
const tessaliq = Tessaliq.init({
apiKey: process.env.TESSALIQ_API_KEY,
// optional: override the API endpoint
baseUrl: 'https://api-staging.tessaliq.com',
});
API keys are issued per organization; you get them via the onboarding
flow (POST /v1/register + email OTP) once that is open
to pilots, or directly via email exchange during Alpha.
3. Run a full verification flow
The SDK wraps session creation, wallet interaction (Digital Credentials API with deep-link fallback), and receipt retrieval into a single call.
// In a React / Next.js / plain JS frontend
async function runAgeGate() {
try {
const result = await tessaliq.requestCredential({
policy: 'av_age_18_plus',
// optional: tie the verification to your own session ID
externalRef: 'order_12345',
});
if (result.claims) {
// The server verified the presentation against the policy.
// No birth date is exposed — only that age_over_18 is true.
await unlockContentForUser(result.sessionId);
} else {
showRetry();
}
} catch (err) {
// handle known error codes: see next section
console.error('Verification failed:', err);
showRetry();
}
}
The requestCredential method uses the Digital
Credentials API (navigator.credentials.get with a
digital object) when the browser supports it, and falls
back to a deep-link to the user's wallet app otherwise. The surface
is the same either way.
4. Session lifecycle
stateDiagram-v2
[*] --> created
created --> wallet_requested: wallet fetches request_uri
wallet_requested --> presentation_received: wallet POSTs vp_token
presentation_received --> verified: all checks pass
presentation_received --> failed: any check fails
created --> expired: TTL reached (default 300s)
verified --> [*]
failed --> [*]
expired --> [*]
A session's TTL defaults to 300 seconds and is capped at 600. Once
the state is verified or failed, it is
terminal. A receipt is only issued on verified.
5. Error handling
The API returns JSON error objects with stable codes:
| Status | Code | Meaning |
|---|---|---|
| 401 | invalid_api_key | Missing or wrong Authorization: Bearer header |
| 403 | suspended | Organization is suspended; contact support |
| 404 | policy_not_found | The policy name does not exist for this organization |
| 404 | session_not_found | The session ID is invalid or belongs to another organization |
| 410 | session_expired | Session TTL has been reached |
| 429 | — | Rate limit (100 req/min default, configurable per organization) |
// Typical error shape
{
"code": "policy_not_found",
"message": "Policy \"av_age_20_plus\" not found. Use the default \"age_18_plus\" policy or create a custom one.",
"status": 404,
"request_id": "prv_abc123..."
}
Every response carries an X-Request-Id header, echoed
as request_id in error bodies. Include it when
reporting issues via the contact form.
6. Verify a receipt server-side
The signed receipt can be verified offline against Tessaliq's published JWKS. This matters when you store receipts for audit purposes and want to prove later that the verification was real.
// Node.js example — uses jose@5+
import { jwtVerify, createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://api.tessaliq.com/v1/keys/jwks')
);
async function verifyReceipt(receiptJwt) {
const { payload } = await jwtVerify(receiptJwt, JWKS, {
issuer: 'https://api.tessaliq.com',
});
// payload.verified === true if the underlying presentation passed.
// payload.policy tells you which policy was applied.
// payload.proof_hash is the binding to the exact presentation bytes.
return payload;
} Or with curl, without code:
# 1. Get the JWKS
curl https://api.tessaliq.com/v1/keys/jwks
# 2. Decode the receipt JWT header to find the signing kid
echo $RECEIPT_JWT | cut -d. -f1 | base64 -d | jq
# 3. Verify using jose-util, step, or your favorite CLI 7. Full Node / Express example
import express from 'express';
import { Tessaliq } from '@tessaliq/sdk-web';
import { jwtVerify, createRemoteJWKSet } from 'jose';
const app = express();
app.use(express.json());
const tessaliq = Tessaliq.init({
apiKey: process.env.TESSALIQ_API_KEY,
baseUrl: 'https://api.tessaliq.com',
});
const JWKS = createRemoteJWKSet(
new URL('https://api.tessaliq.com/v1/keys/jwks')
);
// Frontend calls this to start a verification
app.post('/age-gate/start', async (req, res) => {
const result = await tessaliq.requestCredential({
policy: 'av_age_18_plus',
externalRef: req.body.orderId,
});
res.json({ sessionId: result.sessionId, receipt: result.receipt });
});
// Later, backend stores the receipt and optionally re-verifies it
app.post('/age-gate/archive', async (req, res) => {
const { receipt } = req.body;
const { payload } = await jwtVerify(receipt, JWKS, {
issuer: 'https://api.tessaliq.com',
});
// persist payload.sub (session:...), payload.proof_hash, payload.iat
await db.receipts.insert(payload);
res.json({ ok: true });
});
app.listen(3000); 8. Production checklist — to be defined
Will be written jointly with the first production pilot. Items currently on the open list, subject to change:
- Rotate
apiKeyviaPOST /v1/keys/rotateevery 90 days - Pin the JWKS URL and cache with a sane TTL
- Store receipts with their
proof_hashfor audit - Monitor the
X-Request-Idin your logs for cross-service tracing - Subscribe to the changelog to catch breaking changes
Next
To understand what happens inside the API during a call, see how it works. For the full standards coverage and OIDF conformance runs, see compliance.
Questions or ready to pilot? Start a conversation on the contact form.