@aho-sdk/core
v0.1.2
Published
TypeScript/JavaScript SDK for the Aho Verifiable Credentials API
Maintainers
Readme
AhoSdk TypeScript SDK
Official TypeScript/JavaScript SDK for the Aho Verifiable Credentials API.
Installation
npm install @aho-sdk/core
# or
yarn add @aho-sdk/core
# or
pnpm add @aho-sdk/coreQuick Start
import { Issuer } from '@aho-sdk/core';
// Initialize the Issuer client
const issuer = new Issuer({ apiKey: process.env.AHO_ISSUER_API_KEY });
// Issue a credential
const credential = await issuer.credentials.create({
schema_uuid: 'your-schema-uuid',
subject_identifier: '[email protected]',
claims: {
name: 'Jane Doe',
role: 'Engineer'
}
});
console.log(credential.uuid);Clients
The SDK provides the following clients:
| Client | Purpose | API Key Type |
|--------|---------|--------------|
| Account | Manage account settings, domains, and API keys | Account API Key |
| System | System health and status endpoints | System API Key |
| Holder | Manage holder credentials and presentations | Holder API Key |
| Verifier | Create presentation requests and verify credentials | Verifier API Key |
| Issuer | Issue and manage verifiable credentials | Issuer API Key |
| Schemas | Browse and retrieve credential schemas | Schemas API Key |
| Unauthenticated | Public endpoints (no authentication required) | None (public) |
Usage Examples
Issuing Credentials
import { Issuer } from '@aho-sdk/core';
const issuer = new Issuer({ apiKey: process.env.AHO_ISSUER_API_KEY });
// List all schemas
const schemas = await issuer.schemas.list();
for await (const schema of schemas) {
console.log(schema.name);
}
// Create a schema
const schema = await issuer.schemas.create({
name: 'EmployeeBadge',
claims: [
{ name: 'employee_id', type: 'string', required: true },
{ name: 'department', type: 'string', required: true },
{ name: 'hire_date', type: 'date', required: false }
]
});
// Issue a credential
const credential = await issuer.credentials.create({
schema_uuid: schema.uuid,
subject_identifier: '[email protected]',
claims: {
employee_id: 'EMP-12345',
department: 'Engineering',
hire_date: '2024-01-15'
}
});
// Revoke a credential
await issuer.credentials.revoke(credential.uuid, { reason: 'Employee departed' });Verifying Credentials
import { Verifier } from '@aho-sdk/core';
const verifier = new Verifier({ apiKey: process.env.AHO_VERIFIER_API_KEY });
// Create a presentation request
const request = await verifier.requests.create({
name: 'Employment Verification',
query_format: 'dcql',
credentials: [
{
id: 'employee_badge',
format: 'vc+sd-jwt',
claims: [
{ path: ['employee_id'] },
{ path: ['department'] }
]
}
]
});
// Get the QR code for the request (supports 'png', 'svg' formats)
const qr = await verifier.requests.qrCode(request.uuid, 'svg');
// List responses to the request
const responses = await verifier.responses.list(request.uuid);Managing Holder Credentials
import { Holder } from '@aho-sdk/core';
const holder = new Holder({ apiKey: process.env.AHO_HOLDER_API_KEY });
// List credentials
const credentials = await holder.credentials.list('active');
// Create a presentation (selective disclosure)
const presentation = await holder.presentations.create({
credential_uuid: 'credential-uuid',
disclosed_claims: ['name', 'department']
});Account Management
import { Account } from '@aho-sdk/core';
const account = new Account({ apiKey: process.env.AHO_API_KEY });
// Manage domains
const domains = await account.domains.list();
await account.domains.verify(domain.id);
// Manage signing keys
const keys = await account.signingKeys.list();
await account.signingKeys.rotate(key.id);
// Configure webhooks
await account.webhooks.create({
url: 'https://your-app.com/webhooks/aho',
events: ['credential.issued', 'credential.revoked']
});Pagination
List methods return Page objects with async iteration support:
// Iterate through all pages automatically
for await (const credential of issuer.credentials.list()) {
console.log(credential.uuid);
}
// Or handle pages manually
let page = await issuer.credentials.list(1, 50);
while (page) {
for (const credential of page.data) {
console.log(credential.uuid);
}
page = await page.nextPage();
}
// Collect all items
const allCredentials = await issuer.credentials.list().toArrayAll();File Uploads
For endpoints that accept file uploads:
import * as fs from 'fs';
// Upload a file
await issuer.media.upload({
file: fs.createReadStream('document.pdf'),
metadata: { description: 'Employee contract' }
});
// In the browser, use File or Blob
const fileInput = document.querySelector<HTMLInputElement>('#fileInput');
await issuer.media.upload({
file: fileInput.files[0]
});Binary Responses
Some endpoints return binary data (images, PDFs):
import * as fs from 'fs';
// Get QR code as PNG (returns ArrayBuffer)
const pngData = await verifier.requests.qrCode(uuid, 'png');
fs.writeFileSync('qr.png', Buffer.from(pngData));
// Get QR code as SVG
const svgData = await verifier.requests.qrCode(uuid, 'svg');
fs.writeFileSync('qr.svg', Buffer.from(svgData));Error Handling
import {
Issuer,
ValidationError,
AuthenticationError,
NotFoundError,
RateLimitError,
ApiError
} from '@aho-sdk/core';
try {
await issuer.credentials.create(invalidParams);
} catch (error) {
if (error instanceof ValidationError) {
// 422 - Validation failed
for (const fieldError of error.fieldErrors) {
console.log(`${fieldError.field}: ${fieldError.hint}`);
}
} else if (error instanceof AuthenticationError) {
// 401 - Invalid API key
console.log('Check your API key');
} else if (error instanceof NotFoundError) {
// 404 - Resource not found
console.log(`Resource not found: ${error.message}`);
} else if (error instanceof RateLimitError) {
// 429 - Rate limited (SDK auto-retries with exponential backoff)
console.log(`Retry after ${error.retryAfter} seconds`);
} else if (error instanceof ApiError) {
// Other API errors
console.log(`Error ${error.statusCode}: ${error.message}`);
console.log(`Request ID: ${error.requestId}`);
}
}Error Classes
| Error Class | HTTP Status | Description |
|-------------|-------------|-------------|
| AuthenticationError | 401 | Invalid or missing API key |
| ForbiddenError | 403 | Insufficient permissions |
| NotFoundError | 404 | Resource not found |
| ConflictError | 409 | Resource conflict |
| ValidationError | 422 | Request validation failed |
| RateLimitError | 429 | Rate limit exceeded |
| ServerError | 5xx | Server-side error |
| NetworkError | - | Connection/timeout error |
| ApiError | * | Base class for all API errors |
Rate Limiting
The SDK automatically handles rate limits with exponential backoff:
- Idempotent methods (GET, DELETE, PUT): Auto-retry up to 3 times
- Non-idempotent methods (POST, PATCH): Only retry with idempotency key
// Use idempotency keys for safe retries on POST/PATCH
await issuer.credentials.create({
schema_uuid: '...',
claims: { ... },
idempotencyKey: 'unique-request-id'
});Configuration
// Custom configuration
const issuer = new Issuer({
apiKey: process.env.AHO_ISSUER_API_KEY,
baseUrl: 'https://api.aho.com', // Custom base URL
timeout: 60000, // Request timeout in milliseconds
logger: { // Enable debug logging
debug: (msg) => console.debug(msg),
warn: (msg) => console.warn(msg)
}
});Browser Support
The SDK uses the Fetch API and works in modern browsers (Chrome, Firefox, Safari, Edge) as well as Node.js 18+.
TypeScript
The SDK is written in TypeScript and includes full type definitions:
import { Issuer, Page } from '@aho-sdk/core';
// Types are inferred
const credential = await issuer.credentials.get('uuid');
// credential: Record<string, unknown> | null
const page = await issuer.credentials.list();
// page: Page<Record<string, unknown>>Requirements
- Node.js 18+ or modern browser with Fetch API support
- TypeScript 5.0+ (for TypeScript users)
License
MIT
