@halot/sdk
v1.0.11
Published
Shared Halot protocol SDK
Readme
@halot/sdk
@halot/sdk is the public SDK for integrating provider endpoints and requester flows with Halot.
Use it in 2 cases:
- provider apps that want to expose a paid route with
halot() - requester apps that want to automate the x402 flow with
HalotClient
The root import is the public surface:
import { halot, HalotClient } from '@halot/sdk';Install
npm install @halot/sdkWhat It Exports
The main public exports are:
halotHalotClientcreateActorAuthHeadersdecodePaymentRequirementencodePaymentRequirementsignPaymentRequirement- shared types and Zod schemas such as
QuoteSchema,PreparedJobSchema,JobSchema,PaymentRequirementSchema, andPaymentAuthorizationSchema
Provider Middleware
halot() is the provider integration path for Express routes.
It handles:
- quote challenges
- receipt verification
- result reporting
against the Halot facilitator endpoints.
Before you use it
The route must use a real registered serviceId and provider-signed report headers.
The actual order is:
- register the provider
- register the service under that provider
- use the returned
serviceIdin middleware
If you are using middleware for a service, you do not need halot provider run for that same service.
Basic usage
import express from 'express';
import { Wallet } from 'ethers';
import { createActorAuthHeaders, halot, nowIso } from '@halot/sdk';
const app = express();
app.use(express.json());
const providerId = process.env.HALOT_PROVIDER_ID!;
const providerWallet = new Wallet(process.env.HALOT_PROVIDER_PRIVATE_KEY!);
const providerHeaders = async (_req, context) => {
if (context.providerId !== providerId) {
throw new Error(`Assigned provider ${context.providerId} does not match configured provider ${providerId}`);
}
return createActorAuthHeaders(providerWallet, {
actorId: providerId,
role: 'provider',
method: 'POST',
path: '/facilitator/report',
timestamp: nowIso(),
});
};
app.post('/text', halot({
serviceId: 'svc_text_gpt54',
providerHeaders,
}), async (req, res) => {
const { jobId, providerId: assignedProviderId, requesterAddress } = req.halot;
const text = await runProviderLogic(req.body.message, {
jobId,
providerId: assignedProviderId,
requesterAddress,
});
res.json({ text });
});HALOT_PROVIDER_ID is the provider ID returned by provider registration. HALOT_PROVIDER_PRIVATE_KEY is the private key for that provider actor authority. You can load those values from a secret manager or the CLI-generated authority file instead of env; the important part is that /facilitator/report is signed by the same provider actor that owns the registered service.
One route, multiple services
If one route fronts multiple registered services, resolve serviceId from the request:
app.post('/text', halot({
serviceId: (req) => {
switch (req.body.model) {
case 'gpt-5.4':
return 'svc_text_gpt54';
case 'gpt-5.4-mini':
return 'svc_text_gpt54mini';
case 'gpt-5.4-nano':
return 'svc_text_gpt54nano';
default:
throw new Error('Unsupported model');
}
},
providerHeaders,
}), async (req, res) => {
const text = await runProviderLogic(req.body.message);
res.json({ text, model: req.body.model });
});Middleware flow
When a requester calls your middleware route:
- if there is no
x-halot-receipt, the middleware requests a quote and returns402 - the requester signs the payment requirement
- the requester prepares and funds the job through Halot
- the requester retries with
x-halot-receipt - the middleware verifies that receipt through
/facilitator/verify - your handler runs
- on a 2xx handler response, the middleware reports the output through
/facilitator/report
Request context
Inside a middleware-backed route:
app.post('/text', halot({ serviceId: 'svc_text_gpt54' }), (req, res) => {
const { jobId, quoteId, serviceId, providerId, requesterAddress } = req.halot;
const output = runProviderLogic(req.body.message);
res.json({ output });
});Options
halot() supports:
serviceIdproviderHeaderstarget(hostedby default,localfor a local Halot server)reportMaxAttemptsreportRetryDelayMsonError
Requester Client
HalotClient is the requester-side helper.
It automates:
- triggering a
402challenge - decoding and signing the payment requirement
- preparing the job
- funding the prepared job through your adapter
- confirming the funding transaction
- retrying the target endpoint with
x-halot-receipt
import { HalotClient } from '@halot/sdk';
import { Wallet } from 'ethers';
const wallet = new Wallet(process.env.HALOT_PRIVATE_KEY!);
const client = new HalotClient({
wallet: {
address: wallet.address,
signMessage: (message) => wallet.signMessage(message),
},
funding: {
async fund(preparedJob) {
const transactionHash = await fundPreparedJobOnChain(preparedJob);
return { transactionHash };
},
},
defaultNetwork: '0g:testnet',
});
const response = await client.request<{ text: string }>('https://provider.example/text', {
method: 'POST',
body: {
model: 'gpt-5.4',
message: 'Summarize the latest verifier assignment state.',
},
});response includes:
datajobIdstatus
Auth and Payment Helpers
The SDK also exports helpers for:
- actor-signed provider or verifier requests via
createActorAuthHeaders - payment requirement encoding and decoding
- payment requirement signing
import {
createActorAuthHeaders,
decodePaymentRequirement,
encodePaymentRequirement,
signPaymentRequirement,
} from '@halot/sdk';Shared Types and Schemas
You can also import shared runtime types and validators such as:
QuoteSchemaPreparedJobSchemaJobSchemaPaymentRequirementSchemaPaymentAuthorizationSchema
These are useful when you are building custom clients or validating Halot payloads outside the default middleware and client helpers.
