triqai
v0.2.4
Published
Official TypeScript SDK for the Triqai Transaction Enrichment API
Downloads
84,694
Maintainers
Readme
Triqai Node.js SDK
The official Node.js / TypeScript SDK for the Triqai Transaction Enrichment API.
Enrich raw bank transaction descriptions into structured data: merchants, categories, locations, intermediaries, and more.
Installation
npm install triqaiyarn add triqaipnpm add triqaiQuick Start
import Triqai from "triqai";
const triqai = new Triqai("triq_your_api_key");
const result = await triqai.transactions.enrich({
title: "STARBUCKS SEATTLE WA",
country: "US",
type: "expense",
});
console.log(result.data.transaction.category.primary.name);
// => "Food & Dining"
console.log(result.data.entities);
// => [{ type: "merchant", data: { name: "Starbucks", ... } }, ...]Requirements
- Node.js 18+ (uses native
fetch) - Also works in Bun, Deno, and Cloudflare Workers
Configuration
const triqai = new Triqai("triq_your_api_key", {
// Base URL (default: https://api.triqai.com)
baseUrl: "https://api.triqai.com",
// Retry configuration
maxRetries: 3, // default: 3
retryDelay: 500, // base delay in ms (default: 500)
maxRetryDelay: 30_000, // max delay in ms (default: 30000)
// Request timeout in ms (default: 60000)
timeout: 60_000,
// Extra headers for every request
defaultHeaders: {
"X-Custom-Header": "value",
},
// Debug hooks
onRequest: (info) => console.log(`${info.method} ${info.url}`),
onResponse: (info) => console.log(`${info.status} in ${info.durationMs}ms`),
});API Reference
Transactions
Enrich a transaction
const result = await triqai.transactions.enrich({
title: "AMAZON MKTPLACE PMTS AMZN.COM/BILL WA",
country: "US",
type: "expense",
options: {
// Skip specific entity extraction
filters: {
noLocation: true,
noIntermediary: true,
},
// Pre-fill known data to improve accuracy
merchant: {
name: "Amazon",
},
},
});
// API v1.1.11+: `data.transaction.channel` is no longer returned.
// Confidence reasons are dynamic and may include tags like:
// `venue_or_attraction`, `merchant_location_crosscheck_corrected`,
// `merchant_location_crosscheck_recovered`, `merchant_location_mismatch`, `geo_mismatch`.Enrich with idempotency
const result = await triqai.transactions.enrich(
{ title: "STARBUCKS", country: "US", type: "expense" },
{ idempotencyKey: "my-unique-key-123", force: false },
);List transactions
// Get a single page
const page = await triqai.transactions.list({
page: 1,
size: 50,
startDate: "2026-01-01T00:00:00Z",
endDate: "2026-03-01T00:00:00Z",
});
console.log(page.data); // EnrichedTransaction[]
console.log(page.pageInfo); // { page, size, total, totalPages }
// Navigate pages
if (page.hasNextPage) {
const nextPage = await page.nextPage();
}Auto-paginate
const page = await triqai.transactions.list();
for await (const tx of page) {
console.log(tx.id, tx.raw, tx.status);
}Get a transaction
const tx = await triqai.transactions.get("550e8400-e29b-41d4-a716-446655440000");Delete a transaction
const { deleted, transactionId } = await triqai.transactions.delete(
"550e8400-e29b-41d4-a716-446655440000",
);Categories
const { categories, total, categoryVersion } = await triqai.categories.list();
for (const cat of categories) {
console.log(`${cat.name} (level ${cat.level}, type: ${cat.type})`);
}Merchants
const merchant = await triqai.merchants.get("merchant-uuid");
console.log(merchant.name, merchant.website, merchant.icon);Locations
const location = await triqai.locations.get("location-uuid");
console.log(location.formatted, location.structured.city);Intermediaries
const intermediary = await triqai.intermediaries.get("intermediary-uuid");
console.log(intermediary.name); // "PayPal", "Square", etc.Issue Reports
Report an enrichment issue
const report = await triqai.issueReports.create({
transactionId: "tx-uuid",
description: "Merchant was identified incorrectly",
fields: ["entities.merchant.data.name"],
});List issue reports
const page = await triqai.issueReports.list({
status: "pending",
transactionId: "tx-uuid",
});
// Auto-paginate
for await (const report of page) {
console.log(report.id, report.status);
}Get an issue report
const report = await triqai.issueReports.get("report-uuid");Error Handling
All API errors are thrown as TriqaiError. Common HTTP statuses are mapped to typed subclasses:
import Triqai, {
TriqaiError,
AuthenticationError,
ValidationError,
RateLimitError,
InsufficientCreditsError,
NotFoundError,
} from "triqai";
try {
await triqai.transactions.enrich({ title: "", country: "US", type: "expense" });
} catch (err) {
if (err instanceof ValidationError) {
console.log("Field errors:", err.fieldErrors);
// { title: ["Title cannot be empty"] }
} else if (err instanceof RateLimitError) {
console.log("Retry after:", err.rateLimitInfo.retryAfter, "seconds");
} else if (err instanceof AuthenticationError) {
console.log("Check your API key");
} else if (err instanceof InsufficientCreditsError) {
console.log("Top up credits at https://triqai.com/dashboard");
} else if (err instanceof NotFoundError) {
console.log("Resource not found");
} else if (err instanceof TriqaiError) {
console.log(`API error ${err.statusCode}: ${err.message} [${err.code}]`);
console.log("Request ID:", err.requestId);
}
}Error Classes
| Class | Status | When |
| --- | --- | --- |
| AuthenticationError | 401 | Invalid or missing API key |
| InsufficientCreditsError | 402 | No credits remaining |
| AuthorizationError | 403 | Key valid but not authorized |
| NotFoundError | 404 | Resource does not exist |
| ClientDisconnectedError | 499 | Client disconnected before completion |
| DuplicateRequestError | 409 | Idempotency key reused |
| ValidationError | 422 | Request body validation failed |
| RateLimitError | 429 | Rate limit exceeded |
| InternalServerError | 500 | Server error (retried automatically) |
| ServiceUnavailableError | 503 | Service temporarily down (retried automatically) |
| GatewayTimeoutError | 504 | Upstream timeout |
| ConnectionError | - | Network/DNS failure |
| TimeoutError | - | Request exceeded timeout |
Automatic Retries
The SDK automatically retries on transient errors (429, 500, 503, 504, and network errors) with exponential backoff:
- GET and DELETE requests are always retried
- Enrichment calls generate an idempotency key and reuse it across automatic retries. Supply your own
idempotencyKeyto safely retry across separate calls - The
Retry-Afterheader is respected when present - Default: 3 retries, 500ms base delay, 30s max delay
Disable retries:
const triqai = new Triqai("triq_your_api_key", { maxRetries: 0 });Rate Limiting
Rate limit information is available on successful responses. The SDK handles 429 responses automatically, but you can monitor usage via the onResponse hook or rawGet/rawPost/rawDelete methods:
// Option 1: Debug hook
const triqai = new Triqai("triq_your_api_key", {
onResponse: (info) => {
// info.headers contains X-RateLimit-* values
},
});
// Option 2: Raw request with full response
const resp = await triqai.rawGet("/v1/categories");
console.log(resp.rateLimitInfo.remaining); // tokens left
console.log(resp.rateLimitInfo.limit); // max tokens
console.log(resp.rateLimitInfo.concurrencyRemaining); // concurrent slots leftHealth & API Info
// Check API health
const health = await triqai.health();
console.log(health.data.status); // "healthy"
// Get API version and endpoint directory
const info = await triqai.apiInfo();
console.log(info.data.version); // "v1"
console.log(info.data.endpoints);Raw Requests
For endpoints not yet covered by a resource class, or when you need the full HTTP response (headers, rate limit info):
import type { HttpResponse } from "triqai";
// Full response with headers and rate limit info
const resp: HttpResponse<any> = await triqai.rawGet("/v1/categories");
console.log(resp.data); // response body
console.log(resp.rateLimitInfo); // { limit, remaining, ... }Banks
Search stored banks by exact name (case-insensitive), optionally filtered by country of origin:
const result = await triqai.banks.search({ name: "ING", country: "NL" });
for (const bank of result.banks) {
console.log(bank.id, bank.name, bank.country, bank.icon);
}
if (result.pagination.hasMore) {
const nextPage = await triqai.banks.search({ name: "ING", country: "NL", page: 2 });
console.log(nextPage.banks);
}
const bank = await triqai.banks.get("550e8400-e29b-41d4-a716-446655440000");name is required (1–200 characters after trimming). country is an optional ISO
3166-1 alpha-2 code for the bank's origin, not where it operates. Both lowercase
and uppercase country codes are accepted. Search returns candidates because
multiple banks can share a name; it does not select one arbitrarily. No match
returns an empty banks array. A missing bank ID returns 404.
page defaults to 1 (maximum 10,000), and size defaults to 20 (maximum 100).
Results are ordered by country (unknown last), then ID. Keep filters and size the
same on subsequent pages; results may change between requests during a backfill.
Profiles include foundedYear, foundedOn (an exact date when known),
description, icon (logo URL), color, website, and domain. Unknown values
are null. Bank reads require an API key, are rate limited, and cost zero credits.
The catalog starts empty until populated administratively. Lookups only read
stored data, with no fuzzy matching, enrichment, or automatic bank creation.
Pricing
The Triqai API uses a credit-based pricing model. See triqai.com/pricing for details.
TypeScript
This package is written in TypeScript and ships with full type definitions. All request/response types are exported:
import type {
EnrichRequest,
EnrichSuccessResponse,
EnrichedTransaction,
MerchantData,
LocationData,
CategoryInfo,
EntityResult,
TransactionType,
EnrichmentFieldPath,
} from "triqai";Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
MIT - see LICENSE for details.
The Triqai API itself is a commercial product. This SDK is open source, but API usage requires an API key and is subject to Triqai's terms of service.
