Successfully implemented a comprehensive, type-safe API client for the PayNote application based on the OpenAPI 3.0 specification.
frontend/paynote/src/api/
├── index.ts # Main exports & unified API object
├── README.md # Complete documentation & examples
├── lib/
│ ├── http.ts # HTTP client with timeout & error handling
│ └── errors.ts # ApiError & RateLimitError classes
├── routes/
│ ├── networks.ts # GET /networks
│ ├── organizations.ts # CRUD for organizations & accounts
│ ├── wallets.ts # GET /wallets (with filters)
│ ├── categories.ts # List & create categories
│ ├── rules.ts # List & create automation rules
│ ├── counterparties.ts # List counterparties (with type filter)
│ ├── bindings.ts # Contract bindings by chain/org
│ ├── paynotes.ts # PayNotes list & detail
│ ├── analytics.ts # Daily analytics aggregates
│ ├── fx-snapshots.ts # Exchange rate data
│ ├── search.ts # Unified search
│ └── attachments.ts # Attachment metadata creation
└── types/
└── requests.ts # Request DTOs (Create/Update types)
- ✅
GET /networks- List all supported blockchain networks
- ✅
GET /orgs- List user's organizations - ✅
POST /orgs- Create organization - ✅
GET /orgs/{orgId}- Get organization by ID - ✅
PATCH /orgs/{orgId}- Update organization - ✅
DELETE /orgs/{orgId}- Delete organization - ✅
GET /orgs/{orgId}/accounts- List organization members
- ✅
GET /wallets- List wallets (filter by ownerAccountId or address)
- ✅
GET /orgs/{orgId}/categories- List categories - ✅
POST /orgs/{orgId}/categories- Create category
- ✅
GET /orgs/{orgId}/rules- List automation rules - ✅
POST /orgs/{orgId}/rules- Create rule
- ✅
GET /orgs/{orgId}/counterparties- List counterparties (filter by type)
- ✅
GET /bindings- List contract bindings (filter by chainId or orgId)
- ✅
GET /paynotes- List transactions (filter by chainId, address, categoryId, orgId) - ✅
GET /paynotes/{payNoteId}- Get transaction detail
- ✅
GET /analytics/daily- Daily aggregates (filter by orgId, date range, chainId)
- ✅
GET /fx-snapshots- Exchange rate snapshots (filter by base currency, timestamp)
- ✅
GET /search- Unified search (filter by query, orgId, docType)
- ✅
POST /orgs/{orgId}/attachments- Create attachment metadata
- ✅ Automatic JSON serialization/deserialization
- ✅ Query parameter building
- ✅ Request timeout (default 30s, configurable)
- ✅ Typed error handling with Problem Details (RFC 7807)
- ✅ AbortController for cancellation
- ✅ Helper methods:
get,post,patch,put,delete
- ✅
ApiErrorclass with helper methods:is(status)- Check specific HTTP statusisAuthError()- Check if 401isForbidden()- Check if 403isNotFound()- Check if 404isConflict()- Check if 409isValidationError()- Check if 400/422getFieldErrors()- Extract field-level validation errors
- ✅
RateLimitErrorclass withretryAfterSecondsproperty
- ✅
OrganizationCreate- Create organization payload - ✅
OrganizationUpdate- Update organization payload - ✅
CategoryCreate- Create category payload - ✅
RuleCreate- Create rule payload - ✅
AttachmentCreate- Create attachment metadata payload
All API methods are fully typed using the entity types from src/types/:
// Primitives
UUID, Address, ENSName, Bytes32, TxHash, ChainId, Wei, UnixTime,
FiatCode, HexColor, IconKey, Slug, URLString, Email, FileId
// Enums
Role, Plan, Visibility, CPType, Status, DocType
// Interfaces
Network, Organization, Account, Wallet, Category, Rule,
Counterparty, ContractBinding, Attachment, PayNote,
PayNoteExpanded, AnalyticsDaily, ExchangeRateSnapshot,
SearchIndex, ProblemDetailimport { api } from "@/api";
// All endpoints available via unified object
const networks = await api.networks.list();
const org = await api.organizations.get(orgId);
const paynotes = await api.paynotes.list({ chainId: 1 });import { api, ApiError, RateLimitError } from "@/api";
try {
const result = await api.organizations.create(data);
} catch (error) {
if (error instanceof RateLimitError) {
// Handle rate limit
} else if (error instanceof ApiError) {
if (error.isValidationError()) {
const fieldErrors = error.getFieldErrors();
// Display validation errors
}
}
}"use client";
import { useEffect, useState } from "react";
import { api } from "@/api";
export function Component() {
const [data, setData] = useState([]);
useEffect(() => {
api.paynotes.list().then(setData);
}, []);
return <div>{/* render data */}</div>;
}The API client can now be integrated into all the pages created in the previous steps:
const paynotes = await api.paynotes.list({ chainId, categoryId });
const networks = await api.networks.list();
const categories = await api.categories.list(orgId);const analytics = await api.analytics.daily({ orgId, from, to });
const recentPayNotes = await api.paynotes.list({ orgId });
const topCategories = await api.categories.list(orgId);const payNote = await api.paynotes.get(payNoteId);const wallets = await api.wallets.list({ address });
const sentPayNotes = await api.paynotes.list({ address });const counterparties = await api.counterparties.list(orgId, { type });const categories = await api.categories.list(orgId);
const rules = await api.rules.list(orgId);
await api.categories.create(orgId, data);
await api.rules.create(orgId, data);const results = await api.search.search({ q: query, docType });const organization = await api.organizations.get(orgId);
const members = await api.organizations.listAccounts(orgId);
const bindings = await api.bindings.list({ orgId });
await api.organizations.update(orgId, data);const networks = await api.networks.list();
const bindings = await api.bindings.list({ chainId });Environment variable for API base URL:
NEXT_PUBLIC_API_BASE_URL=https://api.paynote.appDefault: /api (uses Next.js API routes if not set)
- Implement Next.js API Routes - Create corresponding
/app/apiroute handlers - Add React Query/SWR - Wrap API calls for caching, refetching, mutations
- Add Loading States - Integrate with UI components for better UX
- Add Optimistic Updates - Update UI before server confirmation
- Add Request Interceptors - For authentication tokens, etc.
- Add Response Mocks - For development/testing without backend
src/api/index.ts- Main exports (42 lines)src/api/README.md- Complete documentation (265 lines)src/api/lib/http.ts- HTTP client (130 lines)src/api/lib/errors.ts- Error classes (65 lines)src/api/types/requests.ts- Request DTOs (35 lines)src/api/routes/networks.ts- Networks API (12 lines)src/api/routes/organizations.ts- Organizations API (55 lines)src/api/routes/wallets.ts- Wallets API (25 lines)src/api/routes/categories.ts- Categories API (26 lines)src/api/routes/rules.ts- Rules API (24 lines)src/api/routes/counterparties.ts- Counterparties API (24 lines)src/api/routes/bindings.ts- Bindings API (24 lines)src/api/routes/paynotes.ts- PayNotes API (34 lines)src/api/routes/analytics.ts- Analytics API (27 lines)src/api/routes/fx-snapshots.ts- FX Snapshots API (25 lines)src/api/routes/search.ts- Search API (24 lines)src/api/routes/attachments.ts- Attachments API (21 lines)
Total: 17 files, ~858 lines of code
✅ All API endpoints from OpenAPI spec implemented ✅ Type-safe with full TypeScript support ✅ Error handling with custom error classes ✅ Query parameter support ✅ Request/response DTOs ✅ Comprehensive documentation ✅ Ready for integration with UI pages ✅ Zero compilation errors