SDK reference
@zyton/pip is the Zyton SDK for TypeScript and JavaScript on Node.js servers. It is a 0.1 preview, so its API may still change before 1.0. The Changelog announces each release. Version 0.2.0, which adds approval requests, is not on npm yet.
Install
npm install @zyton/pip
It needs Node.js 20.19 or later (or 22.12 or later). The package is ESM, and require('@zyton/pip') works too on those versions.
Create a client
import { PipClient } from '@zyton/pip';
const pip = new PipClient({ apiKey: process.env.PIP_API_KEY });
- You never handle tokens. The client exchanges your
pip_sk_test_…orpip_sk_live_…key for an access token that lasts 5 minutes, renews it before it expires, and after a401gets a new token and retries the call once. The key goes only to the token endpoint, and never into an error. - A revoked or rotated key stops working within 5 minutes, when the last token it was exchanged for expires.
- Server side only. A secret key must never reach a browser, so
PipClientrefuses to run in a web page or a Web or Service Worker. await pip.authenticate()exchanges the key straight away and returns who you are:entityId,tenantId(your organisation),keyIdandkeyEnv(sandboxorlive).
Check consent
import { PipClient } from '@zyton/pip';
// Exchanges the key for a short-lived access token by itself.
const pip = new PipClient({ apiKey: process.env.PIP_API_KEY });
// true only while one active grant covers this user, the operation and every category
const allowed = await pip.consent.checkConsent({
subjectRef: 'user_8412',
operation: 'process',
dataCategories: ['email'],
});checkConsent returns true only when one active grant covers exactly this user and grantee, the operation, every data category and (when you pass one) the purpose. It asks the API on every call.
Consent grants: pip.consent
A grant is your statement that one of your end users agreed to something in your app. It lives in your organisation and in your key's environment, and any key of the same organisation and environment can check or revoke it.
recordGrant(input)records one. PasssubjectRef(your own id for the user, never an email address),purpose,dataCategories(such asemail),operations(any ofread,process,derive,share,store,delete) andevidence.consentTextVersion. Optional:appIdfor one of your apps,expiresAt(60 seconds to 10 years ahead) andjurisdiction. Recording the same active grant again returns it withcreated: false, so retries are safe.checkConsent(check)answers yes or no, live. An emptydataCategorieslist is refused.hasConsent(check)gives the same answer with details (grantId,expiresAt,keyEnv) and may reuse an answer for up to a minute. PassmaxAgeMs: 0to always ask the API.listGrants({ subjectRef }),getGrant(grantId)andrevokeGrant(grantId)list one user's grants (newest first), read one (nullwhen it isn't in your organisation and environment) and revoke one (revoked: falsewhen it was already revoked or expired).- Each grant says how it came about. A grant you recorded has
attestation: 'developer_attested',source.type: 'developer', theattestedByKeyIdthat recorded it and yourevidence. From 0.2.0, a grant your end user approved hasattestation: 'end_user_approved'andsource: { type: 'end_user', approvalRequestId, decidedAt, channel }, withattestedByKeyIdandevidencebothnull.
Approval requests: pip.consent (0.2.0)
Approval requests are live on
api.zyton.ai, but 0.2.0 is not on npm yet: until it is, use the raw HTTP calls in Approval requests, which also has the details.
Instead of recording consent on your word, ask the end user: they approve or deny on a Zyton-hosted screen, in their own Zyton account, and an approval becomes a grant that every check honours.
requestApproval(input)sends a request. PasssubjectRef, the end user'semail(optional once their Zyton account is linked),purpose,dataCategoriesandoperations. Optional:expiresInSeconds(5 minutes to 30 days, default 7 days),grantExpiresAt,jurisdictionandevidence(consentTextVersion,reference). It returnsrequestId,status,expiresAt,created(falsefor a repeat of a pending request) andapprovalUrl: the link for sandbox keys, so you can test the flow yourself, and alwaysnullfor live keys, whose link only goes to the end user by email.waitForApproval(requestId, { timeoutMs, signal })polls until the request isapproved,denied,expiredorcancelled, at once and then every 2 seconds, backing off to 10. AftertimeoutMs(default 10 minutes) it throwsApprovalWaitTimeoutError; abortingsignalstops it at once.getApprovalRequest(requestId)reads one request (nullwhen it isn't in your organisation and environment),listApprovalRequests({ subjectRef?, status? })lists them, newest first, andcancelApprovalRequest(requestId)withdraws a pending one.- Over your organisation's daily cap (200 live, 1,000 sandbox; it resets at 00:00 UTC)
requestApprovalthrowsApprovalRateLimitErrorand nothing is created.
Gate code on consent: @zyton/pip/consent-gate
withConsentGate(gate, options, fn)runsfnonly while a grant covers the user, the operation and every data category, checked live on every call. A refusal throwsConsentDeniedErrorandfnnever runs. Each decision is written to your audit trail without thesubjectRef.wrapCallToolHandler(gate, resolve, handler)does the same for everytools/callof an MCP server. A tool you don't map is refused.@modelcontextprotocol/sdkis an optional peer dependency.
The Quickstart shows the gate in a full script.
Audit trail: pip.audit
pip.audit.query({ entityId }) reads the events of your own entity (the entityId that authenticate() returns), newest first: the gate's decisions and events you write with pip.createAuditWriter(). Your organisation's whole trail, including every consent.granted and consent.revoked, is under Audit log for workspace admins and operators. A key can't read organisation-wide events or verify the chain (403).
Verification: pip.verify
verifyConsentToken(tokenCbor) and verifyCertificate(certDer) ask the verification API whether a signed consent token or a certificate is valid.
What a key can't do
A key is for the API only. Key tokens get 403 on billing and on GDPR data subject request (DSAR) routes, and they can't manage keys, passwords, MFA or sessions. A person signed in to this portal does those.
Configuration
| PipClient option | Environment variable | Default |
| --- | --- | --- |
| apiKey | PIP_API_KEY | none (required) |
| host: the base URL for every API | PIP_API_BASE_URL | https://api.zyton.ai |
| timeoutMs: per request | | 30000 |
Every URL must use https:// (plain http:// only for localhost), and the SDK never follows a redirect.
Errors
Every error is a PipError with a code, an errorCode and a context (the HTTP status, the API's error code and more), and instanceof works for each class:
ValidationError: refused before any request, such as a missing key, use in a browser or invalid input.InvalidCredentialsError: the key is wrong, revoked or rotated. For the next minute (doubling up to 15 minutes) the SDK fails fast without sending it again; a newPipClientstarts fresh.ConsentError: a consent call failed.ConsentDeniedErroris the gate's refusal. From 0.2.0,ApprovalRateLimitError(withresetsAtandretryAfterMs) andApprovalWaitTimeoutError(withlastSeen) are consent errors too.NetworkError: a timeout, a refused connection, a redirect or another HTTP error.
Other languages
There is no Python or Go package on a registry yet. From any language, exchange your key at POST https://api.zyton.ai/oauth/token and send the access token you get back; the HTTP API overview shows how.