@pura-ai/sdk
v0.1.1
Published
Official Pura LLM OAuth and delegated inference SDK
Readme
@pura-ai/sdk
· API
2026-10-01 · Proprietary license
Official proprietary SDK for registered Pura LLM integrations. The app receives
OAuth tokens, never the account's internal virtual key. Install after registry publication with npm install @pura-ai/sdk.
Registry status is tracked in the release guide; build locally with pnpm --dir packages --filter @pura-ai/sdk build.
pnpm pack rebuilds the current source automatically. To build installable
artifacts and verify them in isolated consumers, follow
release and integration verification.
Subscription usage exposes monthly_used_percent, monthly_remaining_percent,
weekly_used_percent, weekly_remaining_percent and 100% scope limits.
weekly_share_percent is 25% of the monthly allowance. Budget/cost amounts are
never returned; only the subscription price is monetary. Lifecycle paid-period
snapshots likewise contain percentage allowances and available services.
Browser Connect
import {createConnectButton, completeBrowserConnection} from '@pura-ai/sdk';
const options = {
apiBase: 'https://ai.puradigital.it/v1',
clientId: 'YOUR_REGISTERED_CLIENT_ID',
redirectUri: 'https://YOUR-APP/pura/callback',
};
document.querySelector('#pura-connect')!.append(createConnectButton({
...options, locale: 'it', onError: error => console.error(error),
}));
// On the registered callback page, in the same browser tab:
const tokens = await completeBrowserConnection(options);
// Store OAuth tokens according to your application's session architecture.
// Never put a confidential client secret into browser code.The button uses authorization-code + PKCE S256, random state and per-tab pending flow storage. Return validation happens before exchanging the code. Authorization errors and expired flows require restarting Connect.
Backend integration and inference
For confidential apps, keep the pending authorization and client secret in your
backend session. Use createAuthorization, redirect the user to its url, then
new PuraOAuthClient(options).exchange(callbackUrl, pending) on return.
import {PuraInferenceClient} from '@pura-ai/sdk';
const pura = new PuraInferenceClient({
...options,
// A separate encrypted OAuth TokenStore per connected user.
store: userTokenStore,
});
const result = await pura.chat({
messages: [{role: 'user', content: 'Ciao'}],
});
const usage = await pura.usage();
const response = await pura.streamChat({messages});
// response.body is the native SSE ReadableStream.TokenStore.get/set must persist refresh-token rotation. A client instance
coalesces concurrent refreshes. For multiple workers, implement
TokenStore.withRefreshLock(operation) with a database/application lock bound to
the connected user's grant. The SDK invokes operation under that lock, rereads
the saved tokens, refreshes only if another worker has not already done so, and
persists rotation before releasing the lock. Reads and writes must use the same
transaction/lock context, shared across every instance for that grant. A failed
operation must release the lock; a removed connection must return null.
Do not lock unrelated users together. Without this adapter only refreshes in one
client instance are coordinated. Reuse of a rotated refresh token revokes its
token family. No inference or budget error
triggers a wallet fallback. PuraError exposes HTTP status and the stable error code.
The server remains authoritative for scopes, revocation, model availability and
shared monthly/weekly usage. SDK v0 targets API version 2026-10-01.
Audio transcription
const transcript = await pura.transcribe({
file: audioBlob, filename: 'recording.wav', language: 'it',
});
console.log(transcript.text);Audio uses the same OAuth grant, account-wide pending slots and percentage budgets
as chat. The SDK sends multipart data without overriding its boundary and replays
the complete immutable Blob if access-token renewal is needed. Files must be
nonempty and at most 25,000,000 bytes. Only JSON/verbose JSON responses are supported;
audio realtime/streaming is outside this endpoint. Use capabilities() to check services.chat and services.audio_transcription.
Model selection is configured only on the Pura server, per service.
This endpoint is implemented but remains disabled until Pura configures a
verified EU audio deployment, cost tracking and subscription allowlist. A 503
subscription_transcription_not_configured means it is unavailable; it never
triggers another provider or wallet. No provider credentials belong in this SDK.
Lifecycle webhook verification (receiver contract)
Delivery requires a configured endpoint and Pura’s separately running webhook
worker. These helpers verify received events; they do not register an endpoint. The wire contract uses X-Pura-Signature:
t=<unix-seconds>,v1=<hex-HMAC-SHA256> over UTF-8 timestamp + . + the original
request bytes. Default tolerance is ±300 seconds. Every retry receives a fresh
signed delivery timestamp; event_id, created and app-local sequence stay fixed.
import {verifyWebhook, createWebhookHandler} from '@pura-ai/sdk';
// Use Express raw-body middleware or the native Request.arrayBuffer().
const event = await verifyWebhook(rawBody, requestHeaders, {
signingSecret: serverSigningSecret,
integrationId: registeredClientId,
});
const receive = createWebhookHandler({
signingSecret: serverSigningSecret,
integrationId: registeredClientId,
store: persistentEventStore,
onEvent: async event => applyEventInTheSameTransaction(event),
});
const result = await receive(rawBody, requestHeaders);WebhookEventStore.processOnce(eventId, handler) must lock and execute the handler
in the same database transaction as event-ID persistence, returning false for an
already committed ID. Failures must roll back so delivery can retry. Coordinate all
workers and implement external effects with your own transactional outbox. Return
2xx only after commit (duplicates can return 2xx). The SDK does not independently
ensure delivery ordering; use sequence to reject stale snapshots or detect gaps.
Invalid signatures never reach the store. The verifier binds the payload to your
app, rejects unsupported API versions and rejects credential fields even when
nested. There is deliberately no account-key decryption function.
OAuth responses include verified pura_user_id, persisted alongside tokens. Bind
it to the authenticated local user in your own server-side exchange, then match
lifecycle user.pura_user_id to that saved mapping. Do not accept identity from
an unverified browser submission. SDK refresh rejects an account identity change
before saving new tokens or sending inference.
Integration requests must not include model. Pura maps chat/streaming through
INTEGRATION_CHAT_MODEL and audio through INTEGRATION_TRANSCRIPTION_MODEL in
its server environment. The SDK and API reject client model overrides.
