# API info
Source: https://docs.triqai.com/api-reference/api/api-info
https://api.triqai.com/openapi-public.json get /v1
# List categories
Source: https://docs.triqai.com/api-reference/categories/list-categories
https://api.triqai.com/openapi-public.json get /v1/categories
# Get intermediary
Source: https://docs.triqai.com/api-reference/entities/get-intermediary
https://api.triqai.com/openapi-public.json get /v1/intermediaries/{id}
# Get location
Source: https://docs.triqai.com/api-reference/entities/get-location
https://api.triqai.com/openapi-public.json get /v1/locations/{id}
# Get merchant
Source: https://docs.triqai.com/api-reference/entities/get-merchant
https://api.triqai.com/openapi-public.json get /v1/merchants/{id}
# Health check
Source: https://docs.triqai.com/api-reference/health/health-check
https://api.triqai.com/openapi-public.json get /health
# Root health check
Source: https://docs.triqai.com/api-reference/health/root-health-check
https://api.triqai.com/openapi-public.json get /
Returns the same health payload as `/health`.
# Create issue report
Source: https://docs.triqai.com/api-reference/issue-reports/create-issue-report
https://api.triqai.com/openapi-public.json post /v1/report-issue
# Get issue report
Source: https://docs.triqai.com/api-reference/issue-reports/get-issue-report
https://api.triqai.com/openapi-public.json get /v1/report-issue/{id}
# List issue reports
Source: https://docs.triqai.com/api-reference/issue-reports/list-issue-reports
https://api.triqai.com/openapi-public.json get /v1/report-issue
# Batch delete transactions
Source: https://docs.triqai.com/api-reference/transactions/batch-delete-transactions
https://api.triqai.com/openapi-public.json delete /v1/transactions/batch
Delete multiple transactions in a single request. Provide exactly **one** of:
- `all: true` — delete every transaction for the organization
- `afterDate` — delete all transactions created on or after the given ISO 8601 date
- `ids` — delete specific transactions by UUID (max 1000)
Associated KV cache entries and DLQ entries are purged after deletion.
# Count transactions
Source: https://docs.triqai.com/api-reference/transactions/count-transactions
https://api.triqai.com/openapi-public.json get /v1/transactions/count
Returns the total number of transactions for the authenticated organization.
Optionally filter by `afterDate` to count only transactions created on or after a given date.
# Delete transaction
Source: https://docs.triqai.com/api-reference/transactions/delete-transaction
https://api.triqai.com/openapi-public.json delete /v1/transactions/{id}
# Enrich transaction
Source: https://docs.triqai.com/api-reference/transactions/enrich-transaction
https://api.triqai.com/openapi-public.json post /v1/transactions/enrich
Enriches a transaction and returns structured transaction + entity data.
# Get transaction
Source: https://docs.triqai.com/api-reference/transactions/get-transaction
https://api.triqai.com/openapi-public.json get /v1/transactions/{id}
# List transactions
Source: https://docs.triqai.com/api-reference/transactions/list-transactions
https://api.triqai.com/openapi-public.json get /v1/transactions
# Authentication
Source: https://docs.triqai.com/authentication
Learn how to authenticate with the Triqai API
All Triqai API requests require authentication using an API key. This guide covers how to obtain, use, and manage your API keys securely.
## API Key Overview
Triqai uses API keys to authenticate requests. Each key is associated with an organization and determines:
* **Access permissions**: Which endpoints you can access
* **Rate limits**: How many requests you can make per minute
* **Credit usage**: Which organization's credits are consumed
## Key Formats
Triqai API key format is as follows: `triq_xxxxx...`
## Getting Your API Key
Go to [triqai.com/login](https://www.triqai.com/login) and sign in.
In your dashboard, find the API Keys section.
Copy your API key. You can generate additional keys if needed.
## Using Your API Key
Include your API key in the `X-API-Key` header with every request:
```typescript Node.js theme={null}
import Triqai from "triqai";
const triqai = new Triqai("triq_your_api_key_here");
const result = await triqai.transactions.enrich({
title: "STARBUCKS NYC",
country: "US",
type: "expense",
});
```
```bash cURL theme={null}
curl -X POST https://api.triqai.com/v1/transactions/enrich \
-H "Content-Type: application/json" \
-H "X-API-Key: triq_your_api_key_here" \
-d '{"title": "STARBUCKS NYC", "country": "US", "type": "expense"}'
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.triqai.com/v1/transactions/enrich',
headers={
'Content-Type': 'application/json',
'X-API-Key': 'triq_your_api_key_here'
},
json={
'title': 'STARBUCKS NYC',
'country': 'US',
'type': 'expense'
}
)
```
## Authentication Errors
If authentication fails, you'll receive a `401 Unauthorized` response:
```json theme={null}
{
"success": false,
"error": {
"code": "authentication_error",
"message": "Invalid or missing API key"
},
"meta": {
"generatedAt": "2026-01-19T10:30:00Z",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a123",
"version": "1.3.13"
}
}
```
### Common Authentication Issues
| Error | Cause | Solution |
| --------------- | ---------------------------------- | ------------------------------------- |
| Missing API key | No `X-API-Key` header provided | Add the header to your request |
| Invalid API key | Key doesn't exist or is malformed | Check for typos; regenerate if needed |
| Revoked API key | Key has been revoked | Generate a new key in the dashboard |
| Invalid format | Key doesn't match expected pattern | Ensure key starts with `triq_` |
## Security Best Practices
Never expose your API key in client-side code, public repositories, or logs.
### Do's
* Store API keys in environment variables
* Use server-side code to make API requests
* Rotate keys periodically
* Use separate keys for development and production
* Monitor API usage in your dashboard
### Don'ts
* Commit API keys to version control
* Include keys in client-side JavaScript
* Share keys via insecure channels
* Use production keys for testing
### Environment Variables
Store your API key in environment variables:
```bash .env theme={null}
TRIQAI_API_KEY=triq_your_api_key_here
```
Then access it in your code:
```typescript Node.js theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
```
```python Python theme={null}
import os
api_key = os.environ.get('TRIQAI_API_KEY')
```
```go Go theme={null}
apiKey := os.Getenv("TRIQAI_API_KEY")
```
## Managing API Keys
### Rotating Keys
If you suspect a key has been compromised:
1. Generate a new key in your dashboard
2. Update your application to use the new key
3. Revoke the old key once the new one is active
### Multiple Keys
You can create multiple API keys for different purposes:
* **Production key**: For your live application
* **Development key**: For local development
* **CI/CD key**: For automated testing pipelines
* **Partner keys**: For third-party integrations
## Organization Context
API keys are scoped to organizations:
* Each key belongs to exactly one organization
* All requests authenticated with a key are attributed to that organization
* Credits are deducted from the organization's balance
* Rate limits are applied per organization
## Next Steps
Understand request limits per plan
Learn how credit consumption works
# Changelog
Source: https://docs.triqai.com/changelog
Product updates and announcements for the Triqai enrichment API
## Merchant identity integrity
### Fixed
* Preserved recognized merchant names, websites, logos and identity confidence when a similar parent belongs to a different business.
* Prevented rejected parent associations from returning through cached transactions or merchant reuse.
## Entity persistence reliability
### Fixed
* Kept enriched merchant and location details available during brief database connection interruptions.
## Transaction refresh reliability
### Fixed
* Improved reliability when refreshing recognized payment processor transactions.
## Payment processor recognition
### Fixed
* Made recognized payment processor results consistent when optional profile lookups are slow or unavailable.
## Payment processor recognition
### Fixed
* Kept recognized payment processors in enrichment results when optional business-profile details are unavailable.
## Payment processor recognition
### Fixed
* Kept production verification aligned with the merchant and payment processor returned for processor-prefixed transactions.
## Payment processor recognition
### Added
* Recognized Nyx and SimplePay in transaction descriptions that place the payment processor before the merchant.
### Fixed
* Kept Tifon and BudapestGO as the merchant while returning the payment processor separately.
## Enrichment freshness
### Fixed
* Rechecks reported transactions without reusing earlier transaction, merchant, or descriptor analysis.
* Protects reported-transaction rechecks with additional access controls.
## Enrichment freshness
### Fixed
* Rechecks reported transactions when an earlier merchant or category result is incomplete or incorrect.
* Restores corrected categories when an affected transaction is processed again.
## Release metadata reliability
### Fixed
* Kept runtime health and API specification version metadata aligned with each deployed release.
* Added release validation to prevent mismatched version metadata from reaching production.
## Database reliability
### Fixed
* Improved cache and authentication reliability during brief database connection interruptions.
* Reduced connection setup overhead for enrichment cache lookups.
## Authentication reliability
### Fixed
* Kept API authentication available during brief rate-limit coordination interruptions.
## Wallet and payment processor recognition
### Added
* Recognized short-form Alipay transaction prefixes (`ALP*`, `ALP-`, `ALP/`, `ALP `) used by banks in Southeast Asia, the Middle East, and other regions.
* Extended deterministic title matching to identify Alipay and other wallet-prefixed transactions regardless of country, reducing processing time for these transactions.
* Added support for configuring known short-form wallet abbreviations that receive relaxed matching when they appear at the start of a transaction description.
### Fixed
* Improved identification of wallet-only QR code payments that contain no merchant name, preventing generic reference numbers from being treated as merchants.
* Corrected display name normalization for Alipay variants (`Ali Pay`, `ALP`, `ALIPAY`) to consistently resolve to the canonical name.
* Prevented wallet abbreviations from being misidentified as merchant names or person-to-person recipients when a wallet intermediary is already detected.
## Enrichment reliability hotfix
### Fixed
* Improved delivery of successful enrichment results during brief service congestion.
* Prevented slow coordination checks from consuming the request's processing window.
* Improved deadline handling for optional entity reuse and fallback categorization.
## Background processing
### Changed
* Improved background handling after a successful enrichment.
## Enrichment freshness
### Fixed
* Rechecks earlier unresolved merchant and location results when a newer API version retries a low-confidence transaction.
* Continues reusing current and successful entity results to avoid unnecessary processing.
## Transaction description accuracy
### Fixed
* Recovered merchant and address details when an address-shaped description previously returned no entities.
* Applied the same safe recovery to earlier cached description analysis results.
## Transaction description accuracy
### Fixed
* Improved merchant and location identification for descriptions containing a business name followed by a numbered address.
* Prevented address text from being mistaken for a person-to-person recipient when no transfer or payment-platform signal is present.
## Enrichment freshness
### Fixed
* Rechecks older low-confidence transaction results when newer enrichment capabilities may identify the merchant.
* Keeps current and intentionally filtered results cached to avoid unnecessary processing.
## Merchant identification accuracy
### Changed
* Improved merchant identification for compact transaction descriptions whose business names are commonly written as separate words.
* Improved local business matching when independent web and location results consistently identify the same company.
### Fixed
* Preserved valid merchant matches when an official website cannot be established.
## AI failure diagnostics hotfix
### Fixed
* Prevented exhausted request deadlines from being reported as missing AI-provider configuration.
* Removed duplicate merchant-enrichment alerts for AI availability failures already reported by the provider layer.
* Preserved actionable error reporting when no AI-provider secret is actually configured.
## Search reliability
### Fixed
* Improved enrichment continuity when search services take longer than usual to respond.
* Prevented slow search requests from extending beyond their allotted response window.
* Reduced duplicate recovery traffic during temporary search-service slowdowns.
### Changed
* Added additional capacity protection for search traffic and improved reliability monitoring.
## Database and cache reliability hotfix
### Fixed
* Prevented oversized transaction titles from exceeding Cloudflare KV's key-size limit.
* Reduced database cache-read round trips and kept optional entity lookups inside their request deadline.
* Gave deferred entity persistence its own background database lifecycle and connection budget.
## Enrichment deadline reliability
### Fixed
* Prevented late enrichment retries from exceeding the request's remaining response budget.
* Improved successful response delivery when entity storage is temporarily slow.
* Reduced the time spent waiting for optional entity-cache lookups during database degradation.
### Changed
* Improved failure diagnostics for correlating provider activity with affected enrichment requests.
## Service observability maintenance
### Changed
* Expanded service monitoring for faster identification of API, enrichment-quality, and upstream-provider regressions.
* Improved provider reliability reporting across successful attempts, timeouts, rate limits, and fallback recovery.
## Enrichment latency hotfix
### Fixed
* Prevented optional search recovery from consuming the remaining response budget.
* Improved graceful partial enrichment when search services remain slow.
## Search recovery hotfix
### Fixed
* Improved recovery when both active search services time out during the same enrichment request.
* Kept the additional recovery work bounded to protect response latency and service capacity.
## Search reliability hotfix
### Fixed
* Improved recovery when preferred search services are simultaneously slow.
* Kept search recovery bounded to protect response latency and service capacity.
### Changed
* Search requests now use Serper and Autom; Exa is disabled.
* Improved diagnostics to distinguish upstream timeouts from rate limiting and track successful recoveries.
## Enrichment reliability
### Fixed
* Improved enrichment continuity when multiple search services respond slowly at the same time.
* Preserved a complete response window for the final search fallback during temporary upstream delays.
### Changed
* Improved search-fallback diagnostics for faster identification of isolated upstream timeouts.
## Service reliability
### Fixed
* Improved continuity when external search services slow down or temporarily limit traffic.
* Improved recovery checks so isolated upstream failures do not interrupt healthy provider traffic.
* Improved processing-provider capacity selection using provider-specific limits.
### Changed
* Expanded service telemetry for more accurate provider-capacity and reliability reporting.
* Updated supporting dependencies to security-patched releases.
## Enrichment reliability
### Fixed
* Prevented routine income enrichment safeguards from being reported as service errors when the final category is valid.
## Service continuity
### Fixed
* Improved request recovery during routine service updates and temporary infrastructure moves.
## Service continuity
### Fixed
* Improved request continuity during routine service updates.
## Maintenance reliability
### Fixed
* Improved cache-cleanup safety checks before account data maintenance begins.
* Avoided unnecessary cache deletion requests when no matching records are present.
## Categorization consistency
### Fixed
* Prevented an earlier transaction direction from affecting categories returned for later transactions involving the same merchant.
* Improved automatic recovery when a stored merchant category conflicts with the requested transaction direction.
* Ensured related merchant profiles consistently use the published category hierarchy.
* Clarified which transaction identifier to use when reporting an enrichment issue.
* Updated supporting framework dependencies to security-patched releases.
## Enrichment reliability hotfix
### Fixed
* Prevented slow entity lookups from exhausting the enrichment request budget.
* Improved graceful fallback when optional AI analysis or validation retries are temporarily unavailable.
## Categorization improvements
### Fixed
* Improved merchant matching for transactions involving delivery platforms and support URLs.
* Ensured returned category hierarchies and names consistently follow the published categorization schema.
## Reliability improvements
### Fixed
* Improved API reliability during temporary service interruptions and high-traffic periods.
## Reliability improvements
### Fixed
* Improved API reliability and response times during temporary service interruptions and high-traffic periods.
## API stability improvements
### Fixed
* Reduced intermittent slow responses when enrichment requires multiple data lookups.
* Improved enrichment response times by more effectively selecting available processing providers.
* Improved enrichment reliability by routing requests away from temporarily degraded providers.
### Changed
* Improved recovery behavior during temporary database connectivity issues.
* Improved fallback enrichment processing to maintain throughput during high-traffic periods.
## Enrichment reliability improvements
### Fixed
* Improved API reliability when external enrichment services are slow or temporarily unavailable.
* Reduced intermittent enrichment timeouts caused by incomplete provider responses.
* Improved recovery from timed-out enrichment work to prevent follow-on request failures.
### Changed
* Improved graceful recovery during temporary third-party service interruptions.
* Expanded automatic fallback coverage for web searches.
## Reliability improvements
### Changed
* Improved internal rate limit monitoring and database connection handling for more reliable API performance during high traffic.
## Reliability improvements
### Changed
* Improved internal release validation and provider error monitoring for more reliable API updates.
## Reliability improvements
### Changed
* Improved internal monitoring for requests.
## Reliability improvements
### Changed
* Improved internal latency to database operations.
## Reliability improvements
### Fixed
* Improved enrichment reliability under moderate load by reducing unnecessary AI provider timeouts.
* Improved credit confirmation resilience during deployments, reducing the chance of unconfirmed billing reservations.
* Fixed a connection pool leak that could occur during background parent-merchant resolution.
## High-traffic reliability and persistence improvements
### Fixed
* Fixed intermittent enrichment failures and stalled requests that could occur when many organizations sent requests concurrently.
* Improved transaction-history reliability by durably accepting completed enrichments before returning a successful response.
* Improved automatic recovery from temporary database connectivity issues while preventing one request from affecting another request's processing.
### Changed
* Failed transaction-history writes now retry automatically with duplicate protection, reducing the risk of missing or repeated activity records during infrastructure disruptions.
## Reliability maintenance
### Changed
* Improved internal accuracy of AI with new model configuration.
## Reliability maintenance
### Changed
* Improved internal latency to database operations.
## Reliability maintenance
### Changed
* Improved internal database read/write performance and reliability.
## Reliability maintenance
### Changed
* Improved internal database handling and request recovery for more reliable API processing during temporary service slowdowns.
## Reliability maintenance
### Changed
* Improved internal request handling for better reliability during temporary infrastructure slowdowns.
## Maintenance update
### Changed
* Improved internal service architecture for faster iteration on enrichment features and reliability improvements.
## Maintenance update
### Changed
* Improved internal maintenance tooling for external dependencies.
## Enrichment reliability improvements
### Fixed
* Improved enrichment reliability during high traffic and temporary database slowdowns, reducing avoidable timeouts and improving fallback behavior when upstream providers are slow.
## Enrichment stability and load balancing improvements
### Fixed
* Fixed intermittent enrichment failures that could occur when multiple requests were processed concurrently within the same service instance, improving reliability under sustained traffic.
### Changed
* Improved load balancing accuracy for providers that enforce concurrent request limits, reducing unnecessary failovers and improving throughput during high-traffic periods.
## New AI infrastructure improvements
### Fixed
* Improved reliability in periods of high traffic for the AI infrastructure.
## Reliability monitoring improvements
### Fixed
* Improved internal monitoring for rate limits and enrichment errors, helping the API team spot real reliability issues more quickly while reducing duplicate operational alerts.
## Maintenance update
### Changed
* Improved internal maintenance tooling for database-backed checks, helping keep release validation and audits reliable without changing production API behavior.
## Place detection stability fixes
### Fixed
* Resolved recurring place detection interruptions during traffic bursts. The place detection service no longer performs heavy warm-up work while serving live requests, removing the timeouts that intermittently disabled place detection for 30-second windows.
* Place detection retries now get a fairer time budget, improving recovery when an initial attempt is slow during scale-out.
## Enrichment reliability and stability improvements
### Fixed
* Enrichment requests no longer fail when a cached result is temporarily unavailable; processing continues normally and recovers automatically.
* Improved place detection stability after brief service interruptions, reducing false detection outages.
* Faster switchover to a fallback web search provider when the primary provider is slow, improving enrichment response times.
* Improved resilience to brief database connection interruptions during enrichment lookups.
* Slow cache lookups can no longer delay enrichment responses.
## Merchant confidence accuracy improvements
### Added
* Merchant results are now corroborated against the independently found location for the same transaction: when both clearly agree on the same real-world business, merchant confidence reflects that verification instead of staying conservatively low.
* New confidence reason codes (`location_corroborated`, `location_domain_corroborated`, `location_title_corroborated`) explain when and why a merchant was verified by its location result.
### Changed
* Soft confidence penalties (such as broad or ambiguous merchant names) are lifted when the location result confirms the exact business, improving confidence accuracy for small local merchants.
* Merchant websites surfaced by the location result can now be attached when verified as merchant-owned; booking-platform, directory, and venue-hosted pages are never attached.
## Enrichment reliability improvements
### Changed
* Improved enrichment response times when an upstream search provider is slow or degraded.
* Improved provider failover so enrichment can recover more reliably before timing out.
* Added more detailed cache performance monitoring to help keep response times stable.
## Performance and reliability fixes
### Fixed
* Improved enrichment response times by restoring the previous stable place detection route.
* Reduced false cache write timeout alerts during periods of higher load.
* Improved reliability for concurrent cached enrichment lookups, reducing unnecessary reprocessing.
* Corrected production environment tagging in error monitoring.
* Reduced duplicate upstream provider rate-limit alerts during provider incidents while still tracking suppressed events.
## Scaling and high-concurrency reliability improvements
### Changed
* Improved enrichment capacity coordination across all running instances, allowing higher sustained request rates without upstream provider rate-limit errors.
* Upstream provider backoffs are now shared instantly across all instances, reducing failed enrichment attempts during traffic spikes.
* Request load is now spread across healthy AI capacity proactively instead of all at once after saturation.
* Reduced authentication cache contention during request bursts for the same organization.
* Faster and more reliable internal place detection calls during scale-out
## Enrichment stability and recognition improvements
### Changed
* Improved enrichment reliability and throughput during higher traffic periods.
* Reduced false internal service disruptions during transient upstream capacity limits.
## Merchant domain accuracy improvements
### Changed
* Improved merchant website matching to reduce cases where third-party listing, directory, map, or profile pages are selected as merchant domains.
* Added more consistent entity reasoning codes and explanations in enrichment responses, including cached and previously saved results.
## Enrichment stability and recognition improvements
### Changed
* Improved enrichment reliability and throughput during higher traffic periods.
* Improved handling of slower transaction-title analysis to reduce premature timeouts.
### Fixed
* Improved recognition of Chinese payment references in transaction titles.
## Enrichment availability improvements
### Changed
* Improved enrichment availability when individual processing providers are slow or temporarily limited.
* Improved consistency of enrichment responses during short-lived upstream service issues.
## Load balancing improvements
### Fixed
* Internal load balancing improvements to reduce the impact of temporary upstream capacity limits.
## Merchant accuracy and stability improvements
### Fixed
* Improved merchant brand matching to reduce rare cases where unrelated businesses could be grouped together.
* Improved enrichment stability during temporary upstream capacity limits.
## Minor stability improvements
### Fixed
* Improved enrichment reliability during brief database slowdowns or high-load periods.
* Reduced rare delays when using cached enrichment and entity results.
## Codebase optimization improvements
### Changed
* Improved internal maintainability of the enrichment pipeline, cache handling, and internal service routes.
## Database query timeout improvements
### Changed
* Improved enrichment response times during temporary database slowdowns.
* Improved enrichment reliability under high traffic by reducing the impact of slow queries on unrelated requests.
## Enrichment persistence reliability improvements
### Fixed
* Improved reliability of saved enrichment results during temporary database connectivity issues.
* Improved consistency of cached enrichment results and activity history during short-lived traffic spikes.
* Reduced rare cases where merchant resolution or enrichment follow-up processing could be delayed after a transient persistence failure.
## Reliability and location resilience improvements
### Changed
* Improved enrichment reliability when location detection or coordinate-based place resolution is temporarily slow or unavailable.
* Improved transaction dissection and AI-assisted enrichment completion rates for more complex or slower-to-resolve transaction titles.
* Improved request handling during traffic bursts and temporary service slowdowns.
### Fixed
* Reduced rare enrichment misses caused by transient location lookup failures.
* Reduced occasional request delays or throttling inconsistencies during short-lived high-traffic periods.
## Enrichment response-time improvements
### Changed
* Improved enrichment response times, especially for new transactions that require merchant matching.
* Improved consistency when the same transaction is enriched multiple times at once.
### Fixed
* Reduced rare delays for repeated enrichment requests during high-traffic periods.
## Enrichment response-time and high-traffic reliability improvements
### Changed
* Improved response times for transaction enrichment, especially for repeated or concurrent requests with the same transaction details.
* Improved enrichment latency for common Chinese payment titles, including Alipay, WeChat Pay, Tenpay, UnionPay, JD Pay, Huabei, and bank-transfer formats.
* Improved enrichment reliability during high-traffic periods by reducing request-path waiting and cache/database contention.
## Parent-merchant resolution speed and identity-consistency improvements
### Changed
* Improved enrichment response speed for transactions where a merchant brand is already known, by resolving parent-merchant identity earlier in the pipeline.
* Improved merchant identity consistency in enrichment responses - parent-merchant name, logo, and category are now only applied when the association has been confirmed, reducing cases where an unverified brand identity could appear in results.
## Platform-derived category signals and pre-auth rate-limiting refinements
### Added
* Known platform intermediaries such as Uber Eats, DoorDash, Deliveroo, Wolt, Instacart, Getir, Lazada, and Shopee can now contribute deterministic transaction categories such as Food Delivery, Groceries, and Online Marketplaces.
* Added `platform_category_match` as a category confidence reason for platform-derived category results.
### Changed
* Improved category accuracy for delivery, grocery, and marketplace platform transactions so known platform categories can outweigh weaker merchant-derived classifications.
* Refined pre-auth rate limiting so missing or malformed API keys are handled separately from valid-format API key cache misses, reducing false positives for legitimate authenticated traffic while still protecting authentication lookups.
### Fixed
* Prevented duplicate platform category signals from inflating category weighting when the same intermediary is encountered through both cached and newly enriched data.
## Upstream lookup reliability improvements
### Changed
* Improved lookup reliability during upstream rate limits and temporary outages.
## P2P disambiguation and parent-merchant resolution reliability improvements
### Changed
* Improved person-versus-merchant disambiguation for transfer-style transactions by adding P2P intermediary context (platform, wallet, and transfer-token signals) to merchant analysis.
* Improved P2P routing so person-detected payees keep intermediary platform context, leading to more complete peer-to-peer enrichment results.
* Improved parent-merchant resolution reliability during enrichment with a more resilient retry path.
### Fixed
* Prevented merchant-stage P2P placeholders from overwriting valid intermediary-derived P2P recipient data.
* Ensured merchant `entities[].data.id` remains canonical at the parent-merchant level when a parent profile is temporarily unavailable.
* Fixed parent-merchant redirect lookups to apply consistently for merchants missing a stored parent ID.
## Merchant category response field and enrichment quality improvements
### Added
* `GET /v1/merchants/{id}` now includes the merchant's `category` field with primary, secondary, and tertiary classification (same `CategoryStructure` format as enrichment responses).
### Changed
* Improved merchant name normalization internally in the enrichment pipeline.
* Improved merchant confidence scoring for single-token acronyms and abbreviations by using fuzzy domain-anchor matching against search results and resolved domains.
* Location enrichment now skips purely numeric store-ID hints because of misclassification of enrichment results.
### Fixed
* Merchant logos are now more likely to show if existing entity internally has no logo.
## Enrichment responsiveness and merchant normalization performance improvements
### Changed
* Faster response times for common enrichment traffic, especially when similar transaction patterns repeat.
* Better responsiveness during high-traffic periods through more efficient capacity checks.
* Faster merchant normalization for clear brand matches, with heavier reconciliation work moved to background processing.
### Fixed
* Reduced response delays when merchant redirect lookups are slow.
* Reduced repeated work for known unresolved redirect mappings with short-lived miss caching.
## Intermediary alias detection and location cross-checking reliability improvements
### Changed
* Improved intermediary detection for short processor aliases in transaction titles, increasing recognition accuracy for delimiter-based formats.
* Improved location cross-checking for store/branch-specific transactions to better validate that selected places match the transaction context.
### Fixed
* Reduced incorrect location matches for store-ID and branch-hint transactions when geographic validation signals are missing or inconsistent.
## Enrichment throughput and parent-merchant consolidation performance improvements
### Changed
* Improved enrichment throughput for concurrent requests, reducing end-to-end latency in high-traffic periods.
* Improved parent-merchant consolidation flow so enrichment results are returned faster while keeping merchant identity consistency.
## Merchant/intermediary recognition and location extraction reliability improvements
### Changed
* Improved merchant detection for noisy and reference-heavy transaction titles.
* Improved intermediary and merchant brand recognition using broader keyword and domain matching.
* Improved location extraction reliability when using selective entity filters.
### Fixed
* Prevented location-only extraction paths from reusing merchant values as location fallbacks.
* Reduced repeated warning noise for invalid AI model overrides in long-running environments.
## Parent merchant matching and confidence improvements
### Changed
* Improved parent merchant matching accuracy for brands that appear on shared marketplace/portal domains.
* More reliable merchant confidence scoring for ambiguous payment-style titles and legal-name search results.
* Better consensus handling when search evidence consistently points to a single merchant brand.
### Fixed
* Reduced incorrect parent-merchant merges for unrelated merchants sharing the same host domain.
## Transaction lifecycle endpoints and classification improvements
### Added
* `GET /v1/transactions/count` endpoint to retrieve the total number of stored transactions, with optional `afterDate` filter.
* `DELETE /v1/transactions/batch` endpoint for bulk deletion by date range or list of IDs (max 1000). Associated cache and all internal organization related entries are purged automatically.
* Per-organization transaction retention setting (`transaction_retention_days`, 1–90 days) to control how long enriched results are cached.
### Changed
* Improved person vs. merchant classification: legal entity suffixes (LTD, LLC, GmbH, B.V., etc.) now strongly favor merchant classification in both title dissection and merchant analysis.
* Improved P2P detection: transfer phrases followed by business names with legal suffixes are no longer misclassified as person-to-person transfers.
* Improved merchant detection signals: company registries, commercial directories, and invoice/reference patterns now contribute to merchant classification confidence.
## Legal-name accuracy and IBAN anonymization improvements
### Changed
* Accuracy improvements for legal names in transaction enrichment `POST /v1/transactions/enrich` pipeline.
* Improved anonymization: IBANs are now always hidden in internal pipeline processing.
## Recurring-payment metadata removal from transaction responses
### Breaking
* Removed the legacy recurring-payment object from enrichment transaction responses.
* Removed recurring-payment enum/type surfaces from the public API contract.
### Migration Notes
* **Action required** if your integration reads or validates the legacy recurring-payment object.
* Update response parsing, DTOs, and schema validation to use `data.transaction.category` and `data.transaction.confidence` only.
* This change does **not** affect billing plan flows in the dashboard and Stripe.
### Changed
* Internal enrichment persistence no longer stores legacy recurring-payment classification fields for transactions and merchants.
## Coordinate-based location context and resolution improvements
### Added
* Core API now accepts GPS coordinates in `options.location.coordinates` as location context for enrichment.
* When coordinates are provided without a city name, the API now resolves them to a nearby place name.
### Changed
* Location enrichment is now more accurate for transactions where the title has little or no clear location text.
* If both coordinates and `cityName` are provided, `cityName` remains the source of truth.
### Fixed
* Prevented country-level matches from being used as location results when resolving coordinates.
* Ensured coordinate-based location hints are not applied when `options.filters.noLocation` is enabled.
## More conservative fallback categorization for IBAN-heavy transfer-like titles
### Changed
* Fallback categorization is now more conservative for transfer-like titles that contain full IBAN-style account references.
* For IBAN-heavy titles, you may see lower confidence and `reference_code` in confidence reasons, even if transfer wording is present.
### Fixed
* Improved reliability of request gating under high-load/throttling scenarios.
* Improved consistency of credit reservation handling when requests are denied by system-wide capacity protections.
## Canonical brand-level merchant IDs
### Breaking
* Merchant `entities[].data.id` now returns the **canonical brand-level merchant ID** (parent merchant UUID), not a store-level/sub-merchant ID.
* If your integration stores or joins on merchant IDs from previous versions, you should treat this as a new canonical ID space.
### Migration Notes
* **Action required** if you persist merchant IDs in your database, analytics model, or downstream joins.
* **No action required** if you only display merchant name/icon/category in responses.
* Recommended approach:
* Store the new `entities[].data.id` as your canonical merchant key.
* Re-map historical references over time by re-enriching recent transactions or by backfilling your local mappings.
### Added
* Parent merchant deduplication: location/store variants of the same brand are grouped under one canonical merchant.
* Responses now return a consistent merchant identity (`id`) and canonical brand profile across transactions for the same brand.
### Changed
* Merchant names and descriptions are now curated at the brand level when multiple merchant observations exist.
* You may see cleaner brand naming (less branch/store-specific noise) while keeping relevant merchant metadata.
## Channel removal and merchant-detection improvements
### Breaking
* Removed `data.transaction.channel` from enrichment responses.
* Removed channel-related enums and shared types from the public API contract and SDK type surface.
### Added
* Platform-domain validation for merchant enrichment. Social/content platform domains are no longer assigned as merchant domains unless the merchant is the platform itself.
* URL-based merchant recovery in title dissection when a merchant is missing but a URL is present in the transaction title.
* New `venue_or_attraction` confidence reason for non-traditional merchants such as venues, attractions, and transport operators.
### Changed
* Improved merchant confidence scoring for abbreviated and concatenated merchant names.
* Broadened merchant classification so payees that provide goods, services, entry, or transport are more consistently recognized as merchants.
* Improved `broad_merchant_name` tagging so known brand names are less likely to be marked as broad/generic.
* Improved merchant domain extraction to avoid selecting review sites, app stores, and social media pages as the merchant domain.
## Merchant-location cross-checking and fallback improvements
### Added
* Post-enrichment merchant-location cross-checking on `POST /v1/transactions/enrich` to detect merchant/location mismatches and automatically re-select better location candidates when available.
* Location recovery path for cases where location initially returns no match but a merchant-aligned place exists in search results.
* New location confidence reasons in responses: `merchant_location_crosscheck_corrected`, `merchant_location_crosscheck_recovered`, `merchant_location_mismatch`, and `geo_mismatch`.
### Changed
* Improved fallback routing for non-P2P transactions: fallback classification now runs when merchant extraction ends in no match, and income classification runs only when a merchant is confidently found.
* More conservative fallback confidence calibration for ambiguous or reference-heavy titles, with stronger routing to `Uncategorized` or `Other Income` in low-confidence cases.
* Improved title dissection for separator-heavy and URL-containing transaction strings.
* Improved country handling with stricter ISO 3166-1 alpha-2 validation and broader country-name/address resolution for multilingual aliases.
### Fixed
* Reduced wrong-branch location matches for similarly named merchants by cross-validating merchant identity against selected location evidence.
* Fixed cases where weak fallback evidence could inflate confidence or produce over-specific categories.
## Intermediary alias inference improvements
### Added
* Alias inference in intermediary post-processing to improve processor/platform recognition from variant names.
### Changed
* Improved intermediary detection reliability for mixed and abbreviated transaction strings.
## Merchant disambiguation and fallback confidence updates
### Added
* Expanded merchant analysis signals with country-aware disambiguation and legal-entity marker handling.
* Expanded fallback confidence reason handling for ambiguous and reference-like titles.
### Changed
* Improved title normalization for merchant analysis and fallback categorization.
* Improved fallback confidence scoring to better reflect uncertainty.
### Fixed
* Negative fallback reason tags now take precedence over positive tags during confidence calibration.
## Enrichment cache performance and observability updates
### Changed
* Faster enrichment retrieval path with cache transaction lookups.
* Improved entity-cache lookup/save performance for lower enrichment latency.
* Added more granular cache phase tracking for enrichment observability.
## New `options` field for enrichment endpoint
### Added
* **Enrichment options** on `POST /v1/transactions/enrich`: new optional `options` field in the request body.
* **Filters** (`options.filters`): set `noMerchant`, `noIntermediary`, or `noLocation` to `true` to skip extraction of that entity type. Useful when you already have the data or want faster responses.
* **Pre-filled merchant** (`options.merchant`): supply `id`, `name`, or `domain` when you know the merchant. The API will use it directly instead of extracting from the title. IDs must exist in our system (returns 422 if not found); names and domains use best-effort lookup.
* **Pre-filled location** (`options.location`): supply `cityName`, `streetName`, `storeNumber`, `physicalLocation`, or `coordinates` to improve location accuracy.
* **Pre-filled intermediaries** (`options.intermediaries`): supply up to 5 intermediaries by `id`, `name`, or `domain` when you know the payment processor(s).
* You cannot combine a filter (e.g. `noMerchant`) with pre-filled data for the same entity type; the request will return 422.
### Changed
* Improved extraction accuracy for transaction titles that include country codes (e.g. `NETFLIX NL`).
* Improved handling of reference-like patterns (payment IDs, order numbers).
* When you pre-fill merchant and skip intermediary + location, the API may bypass title dissection for lower latency.
### Fixed
* Titles ending in a country code (e.g. `NETFLIX NL`) are now parsed correctly instead of being treated as merchant-only.
## Incremental accuracy and performance improvements
### Changed
* Incremental accuracy and performance improvements across the enrichment pipeline.
* Improved entity detection for noisy transaction strings, reducing merchant false negatives.
* Faster cold-start behavior on `POST /v1/transactions/enrich`.
* Tuned confidence reason-tagging to make confidence scores more reliable, especially for merchant and location entities.
* Additional latency improvements in enrichment processing.
# Categories
Source: https://docs.triqai.com/concepts/categories
Understanding Triqai's hierarchical category taxonomy
Triqai uses a comprehensive, hierarchical category system to classify transactions. This taxonomy is designed to be consistent, intuitive, and compatible with industry standards.
## Category Structure
Categories are organized in three levels:
Primary → Secondary → Tertiary
For example:
* **Primary**: Parking
* **Secondary**: Vehicle Services
* **Tertiary**: Transportation
Not every transaction will have all three levels. Triqai assigns the most specific applicable category while ensuring accuracy.
## Category Response
Each enriched transaction includes a category structure with a `confidence` object containing both a numeric `value` and explanatory `reasons`:
```json theme={null}
{
"category": {
"primary": {
"name": "Shopping",
"code": {
"mcc": 5411,
"sic": 5411,
"naics": 445110
}
},
"secondary": {
"name": "Online Shopping",
"code": {
"mcc": 5411,
"sic": 5411,
"naics": 445110
}
},
"tertiary": {
"name": "Marketplace",
"code": {
"mcc": 5411,
"sic": 5411,
"naics": 445110
}
},
"confidence": { "value": 95, "reasons": ["merchant_category_match"] }
}
}
```
### Category Confidence Reasons
The `reasons` array on category confidence uses these tags:
| Tag | Meaning |
| ------------------------- | ---------------------------------------------------------------------- |
| `merchant_category_match` | The identified merchant has a known/linked category mapping |
| `fallback_classification` | Category came from fallback logic rather than strong merchant evidence |
| `p2p_transfer_detected` | Transaction detected as P2P transfer by deterministic rules |
## Industry Codes
Each category includes standardized industry codes:
| Code | Name | Description |
| --------- | --------------------------------------------- | ------------------------------------- |
| **MCC** | Merchant Category Code | 4-digit code used by payment networks |
| **SIC** | Standard Industrial Classification | Legacy classification system |
| **NAICS** | North American Industry Classification System | Modern industry classification |
These codes enable integration with accounting systems, compliance tools, and analytics platforms that expect standardized industry identifiers.
## Expense Categories
Triqai supports 68 expense categories across these primary groups:
* General Merchandise
* Online Shopping
* Clothing & Apparel
* Electronics
* Home & Garden
* Department Stores
* Marketplace
* Restaurants
* Fast Food
* Coffee Shops
* Bars & Nightlife
* Groceries
* Food Delivery
* Gas Stations
* Parking
* Public Transit
* Ride Sharing
* Tolls
* Car Rental
* Airlines
* Streaming Services
* Movies & Theater
* Music
* Gaming
* Sports & Recreation
* Events & Tickets
* Electricity
* Gas
* Water
* Internet
* Phone
* Cable TV
* Insurance
* Healthcare
* Pharmacy
* Fitness & Gym
* Personal Care
* Medical Services
* Bank Fees
* Interest
* Investments
* Taxes
* Insurance Premiums
* Loan Payments
* Hotels & Lodging
* Airlines
* Car Rental
* Travel Agencies
* Vacation Rentals
* Professional Services
* Software
* Education
* Legal Services
## Income Categories
Triqai supports 38 income categories including:
* **Salary & Wages**: Regular employment income
* **Freelance**: Contract and gig work payments
* **Refunds**: Returns and reimbursements
* **Interest**: Bank interest, dividends
* **Transfers**: P2P and account transfers
* **Government**: Benefits, tax refunds
* **Rental Income**: Property rental payments
* **Investments**: Capital gains, dividends
## Category Confidence
Each category assignment includes a confidence object with a `value` (0-100) and `reasons`:
| Range | Interpretation |
| ------ | ------------------------------------------------------ |
| 90-100 | Very high confidence, reliable for automated decisions |
| 70-89 | High confidence, suitable for most use cases |
| 50-69 | Moderate confidence, may benefit from manual review |
| 0-49 | Low confidence, recommend manual verification |
## Fetching All Categories
You can retrieve the complete category taxonomy via the API:
```typescript Node.js theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
const { categories, total, categoryVersion } = await triqai.categories.list();
for (const cat of categories) {
console.log(`${cat.name} (level ${cat.level}, type: ${cat.type})`);
}
```
```bash cURL theme={null}
curl https://api.triqai.com/v1/categories \
-H "X-API-Key: YOUR_API_KEY"
```
The response includes all categories with their hierarchy, descriptions, and industry codes.
## Category Versioning
Triqai's category taxonomy is versioned. The current version is returned in the `meta.categoryVersion` field:
```json theme={null}
{
"meta": {
"categoryVersion": "triqai-2026.01"
}
}
```
When the taxonomy is updated, the version changes. This allows you to detect and adapt to changes in the category structure.
## Best Practices
**Use primary categories for high-level grouping** and secondary/tertiary for detailed analysis. This provides flexibility for different reporting needs.
**Industry codes are mapped per category**, not per merchant. If you need merchant-specific MCC codes, use the payment processor's original data.
## Next Steps
Learn about merchant, location, and intermediary data
Fetch the complete category taxonomy
# Confidence Scores
Source: https://docs.triqai.com/concepts/confidence-scores
Understanding and using confidence scores and reason tags for reliable enrichment
Every enrichment result includes confidence scores that indicate how certain Triqai is about each piece of data. In addition to numeric scores, Triqai provides **reason tags** that explain *why* a score is what it is. Understanding these helps you make better decisions about when to trust automated enrichment and when to require manual review.
## What Are Confidence Scores?
Confidence scores are objects with two fields:
```json theme={null}
{
"value": 92,
"reasons": ["name_closely_matched", "results_consensus"]
}
```
* **`value`**: An integer from 0 to 100 representing certainty
* **`reasons`**: An array of string tags explaining the score
### Score Ranges
* **0**: No confidence (should not be used)
* **50**: Low confidence (uncertain match)
* **75**: Moderate confidence (likely correct)
* **90**: High confidence (very likely correct)
* **100**: Maximum confidence (definitive match)
## Where Scores Appear
Confidence scores appear at multiple levels in the response:
### Overall Transaction Confidence
```json theme={null}
{
"data": {
"transaction": {
"confidence": { "value": 92, "reasons": [] }
}
}
}
```
This represents the overall quality of the enrichment across all modules.
### Category Confidence
```json theme={null}
{
"category": {
"primary": { "name": "Shopping" },
"secondary": { "name": "Online Shopping" },
"confidence": { "value": 95, "reasons": ["merchant_category_match"] }
}
}
```
### Per-Entity Confidence
Each entity in the `entities` array has its own confidence:
```json theme={null}
{
"entities": [
{
"type": "merchant",
"role": "organization",
"confidence": {
"value": 98,
"reasons": ["name_closely_matched", "results_consensus"]
},
"data": { "name": "Starbucks" }
},
{
"type": "location",
"role": "store_location",
"confidence": {
"value": 72,
"reasons": ["city_match", "multiple_plausible_locations"]
},
"data": { "name": "Starbucks - Downtown" }
},
{
"type": "intermediary",
"role": "processor",
"confidence": { "value": 99, "reasons": ["known_processor_match"] },
"data": { "name": "Square" }
}
]
}
```
## Confidence Reason Tags
Reason tags explain what contributed to or detracted from the confidence score. They are divided into several categories.
### Global Reasons
These can appear on any entity type (merchant, location, or intermediary):
| Tag | Meaning |
| ----------------------- | ---------------------------------------------------------------------- |
| `results_consensus` | Multiple independent sources/results point to the same entity |
| `ambiguous_entity` | Multiple plausible candidates; evidence does not uniquely identify one |
| `results_contradict` | Top results disagree on key identity fields, indicating uncertainty |
| `insufficient_evidence` | Not enough reliable evidence to support a strong match |
### Category Reasons
Applied to the category confidence score (deterministic, code-only):
| Tag | Meaning |
| ------------------------- | ---------------------------------------------------------------------- |
| `merchant_category_match` | The identified merchant has a known/linked category mapping |
| `fallback_classification` | Category came from fallback logic rather than strong merchant evidence |
| `p2p_transfer_detected` | Transaction detected as P2P transfer by deterministic rules |
### Merchant Reasons
Applied to merchant entity confidence (global + merchant-specific):
| Tag | Meaning |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `broad_merchant_name` | Extracted merchant string is a generic category term, not a distinct brand name |
| `generic_descriptor` | "Merchant name" is primarily a descriptor rather than a proper noun |
| `name_closely_matched` | Chosen merchant name matches the raw transaction tokens strongly |
| `name_inferred` | Output name differs from extracted name because AI inferred the likely correct name |
| `brand_disambiguated` | AI resolved a brand ambiguity based on evidence |
| `category_consistent_with_context` | Merchant type implied by evidence matches transaction context/category |
| `venue_or_attraction` | Merchant appears to be a venue, attraction, or transport operator and is treated as a valid merchant context |
### Location Reasons
Applied to location entity confidence (global + location-specific):
| Tag | Meaning |
| ---------------------------------------- | ------------------------------------------------------------------------------------- |
| `country_match` | Location country aligns with expected country hints |
| `wrong_country` | Location country does not align with expected country hints |
| `city_match` | City token is present in raw or strong context and matches chosen location city |
| `store_id_match` | Store number/branch ID appears in raw and matches a specific store |
| `single_result_match` | Only one strong relevant place result exists and it matches clearly |
| `identifier_match` | Specific identifier matches (street, phone, postal code, store code) |
| `address_closely_matched` | Address text in results aligns closely with chosen location fields |
| `multiple_plausible_locations` | Several plausible places exist and evidence doesn't uniquely select one |
| `chain_location_disambiguated` | For chains, AI selected a specific branch based on evidence |
| `merchant_location_crosscheck_corrected` | Merchant-location cross-check detected a mismatch and corrected the selected location |
| `merchant_location_crosscheck_recovered` | Location was recovered from alternate search results using merchant-aligned evidence |
| `merchant_location_mismatch` | Merchant identity and selected location evidence conflict, reducing confidence |
| `geo_mismatch` | Geographic signals (city/country/address/coordinates) conflict with expected context |
### Intermediary Reasons
Applied to intermediary entity confidence (global + intermediary-specific):
| Tag | Meaning |
| ------------------------------------ | ----------------------------------------------------------------------- |
| `name_closely_matched` | Intermediary name matches raw tokens strongly |
| `processor_role_disambiguated` | AI resolved whether entity acts as processor/gateway vs actual merchant |
| `platform_vs_merchant_disambiguated` | AI resolved platform vs underlying merchant |
| `known_processor_match` | Deterministic match to internal processor dictionary (prefix/pattern) |
## Score Interpretation
**Interpretation**: Highly reliable for automated decisions **When you see
this**: - Transaction string closely matches known patterns - Multiple data
points confirm the identification - Entity is well-known with clear
signatures **Common reasons**: `results_consensus`, `name_closely_matched`,
`known_processor_match` **Recommended action**: Use directly in your
application without review
**Interpretation**: Suitable for most use cases **When you see this**: - Good
pattern match but some ambiguity - Entity identified with reasonable certainty
* Minor uncertainty in specific fields **Common reasons**: `city_match`,
`brand_disambiguated`, `category_consistent_with_context` **Recommended
action**: Use for display and analytics; consider review for financial
decisions
**Interpretation**: May benefit from manual review **When you see this**: -
Partial pattern match - Multiple possible interpretations - Limited data
points available **Common reasons**: `ambiguous_entity`,
`multiple_plausible_locations`, `broad_merchant_name` **Recommended action**:
Display with caveat or queue for review
**Interpretation**: Requires verification **When you see this**: - Weak or
ambiguous pattern match - Unknown or unusual merchant - Conflicting signals
in transaction **Common reasons**: `results_contradict`,
`insufficient_evidence`, `generic_descriptor` **Recommended action**:
Request user confirmation or manual review
## Confidence by Entity Type
Different entity types typically have different confidence distributions:
| Entity | Typical Range | Notes |
| ---------------- | ------------- | ------------------------------------------ |
| **Merchant** | 70-99 | Well-known merchants score higher |
| **Location** | 50-95 | Store-level matching is harder |
| **Category** | 75-99 | Based on merchant + context |
| **Intermediary** | 90-99 | Distinct patterns, high accuracy |
| **Person** | 85-99 | Extracted directly from transaction string |
## Using Confidence in Your Application
### Threshold-Based Logic
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
const result = await triqai.transactions.enrich({
title: "STARBUCKS NYC",
country: "US",
type: "expense",
});
const threshold = 85;
const autoApprove =
result.data.transaction.confidence.value >= threshold &&
result.data.entities.every((e) => e.confidence.value >= threshold);
const needsReview = result.data.transaction.confidence.value < 70;
```
### Using Reason Tags
```typescript theme={null}
function analyzeConfidence(entity: { confidence: { value: number; reasons: string[] } }) {
const { value, reasons } = entity.confidence;
if (value >= 90 && reasons.includes("results_consensus")) {
return { reliable: true, action: "auto_approve" };
}
if (reasons.includes("ambiguous_entity") || reasons.includes("results_contradict")) {
return { reliable: false, action: "manual_review" };
}
if (reasons.includes("known_processor_match")) {
return { reliable: true, action: "auto_approve" };
}
return {
reliable: value >= 75,
action: value >= 75 ? "auto_approve" : "manual_review",
};
}
const result = await triqai.transactions.enrich({
title: "STRIPE* ACME CORP",
country: "US",
type: "expense",
});
for (const entity of result.data.entities) {
const analysis = analyzeConfidence(entity);
console.log(`${entity.type}: ${analysis.action}`);
}
```
### Displaying Confidence to Users
```typescript theme={null}
function getConfidenceLabel(confidence: { value: number; reasons: string[] }) {
if (confidence.value >= 90) return { text: "Verified", color: "green" };
if (confidence.value >= 70) return { text: "Likely", color: "blue" };
if (confidence.value >= 50) return { text: "Uncertain", color: "yellow" };
return { text: "Unverified", color: "red" };
}
```
### Filtering by Confidence
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "AMAZON MKTPLACE PMTS",
country: "US",
type: "expense",
});
const reliableMerchants = result.data.entities.filter(
(e) => e.type === "merchant" && e.confidence.value >= 80,
);
const flaggedForReview = result.data.entities.filter(
(e) => e.confidence.value < 60 || e.confidence.reasons.includes("ambiguous_entity"),
);
```
## Best Practices
Different applications have different tolerance for errors. A personal
finance app might accept lower confidence than a compliance system.
If incorrect categorization has serious consequences, require higher
confidence thresholds or manual review.
Don't just check the numeric score reason tags like `known_processor_match` or
`results_contradict` give you richer context for decision-making.
Monitor the confidence scores you're seeing. Consistently low scores for
certain transaction types might indicate a need for different handling.
Use the Issue Report API to flag incorrect enrichments. This helps improve
accuracy over time.
## Next Steps
Handle errors and partial results gracefully
Report enrichment issues to improve accuracy
# Transaction Enrichment
Source: https://docs.triqai.com/concepts/enrichment
How Triqai transforms raw transaction data into structured information
Transaction enrichment is the core capability of Triqai. It takes raw, unstructured transaction strings from bank statements and transforms them into clean, structured data that's easy to understand and use.
## The Problem
Bank transaction data is notoriously messy. A simple coffee purchase might appear as:
```
POS 4392 STARBUCKS STORE #1234 NEW YORK NY 10001
```
This string contains useful information: merchant name, store number, location, but it's buried in noise and inconsistent formatting. Every bank formats transactions differently, making it impossible to reliably extract meaning without specialized processing.
## The Solution
Triqai analyzes transaction strings using a combination of:
* **Pattern matching** against known merchant signatures
* **Natural language processing** to extract entities
* **Extensive merchant databases** (150M+ companies)
* **Location intelligence** (10M+ places globally)
* **Machine learning models** for classification and confidence scoring
The result is structured data you can immediately use in your application.
## Enrichment Pipeline
When you submit a transaction for enrichment, it goes through several stages:
The raw transaction string is tokenized and analyzed for structure.
Potential merchants, locations, intermediaries, and other entities are
identified.
Detected entities are matched against our databases of known merchants,
locations, and intermediaries.
The transaction is categorized and additional metadata (such as confidence
signals) is determined.
Confidence scores with explanatory reason tags are calculated for each
entity and field.
## What Gets Enriched
Each enrichment request returns two main sections: **transaction metadata** and an **entities array**.
### Transaction Metadata
Classification and signals about the transaction itself:
* **Category** — Hierarchical spending categories (primary, secondary, tertiary) with MCC/SIC/NAICS codes
* **Confidence** — Overall enrichment confidence with reason tags
### Entities Array
An array of identified real-world entities, each with a `type`, `role`, `confidence`, and `data`:
* **Merchant** — The business behind the transaction (name, logo, website, domain)
* **Location** — The physical place where it occurred (address, coordinates, timezone)
* **Intermediary** — Payment processors, delivery platforms, wallets, or P2P services (Stripe, Venmo, DoorDash, etc.)
* **Person** — Recipient information for P2P transfers (display name)
Only entities that are actually identified appear in the array. If no location is found, no location entity is present. There are no `"status": "no_match"` entries.
## Request Format
A basic enrichment request requires three fields:
```json theme={null}
{
"title": "AMAZON MKTPLACE PMTS AMZN.COM/BILL WA",
"country": "US",
"type": "expense"
}
```
| Field | Type | Required | Description |
| --------- | ------ | -------- | -------------------------------------------------------- |
| `title` | string | Yes | Raw transaction description from bank statement |
| `country` | string | Yes | ISO 3166-1 alpha-2 country code (e.g., "US", "NL", "GB") |
| `type` | string | Yes | Transaction direction: `expense` or `income` |
## Response Structure
The enrichment response uses an **entities array** pattern. Only found entities are included:
```json theme={null}
{
"success": true,
"partial": false,
"data": {
"transaction": {
"category": {
"primary": {
"name": "Coffee Shops",
"code": { "mcc": 5814, "sic": 5812, "naics": 722515 }
},
"secondary": {
"name": "Food & Dining",
"code": { "mcc": 5812, "sic": 5812, "naics": 722511 }
},
"tertiary": null,
"confidence": { "value": 95, "reasons": ["merchant_category_match"] }
},
"confidence": { "value": 92, "reasons": [] }
},
"entities": [
{
"type": "merchant",
"role": "organization",
"confidence": {
"value": 98,
"reasons": ["name_closely_matched", "results_consensus"]
},
"data": {
"id": "...",
"name": "Starbucks",
"alias": ["SBUX"],
"icon": "https://logos.triqai.com/images/starbuckscom",
"website": "https://www.starbucks.com",
"domain": "starbucks.com"
}
},
{
"type": "location",
"role": "store_location",
"confidence": {
"value": 85,
"reasons": ["city_match", "store_id_match"]
},
"data": {
"id": "...",
"name": "Starbucks - Times Square",
"formatted": "1530 Broadway, New York, NY 10036, USA",
"structured": { "city": "New York", "state": "NY", "country": "US" }
}
}
]
},
"meta": {
"generatedAt": "2026-01-15T10:30:00Z",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a123",
"version": "1.3.13",
"categoryVersion": "triqai-2026.01"
}
}
```
Only identified entities appear in the `entities` array. If no intermediary or
location was found, they are simply absent not present with a null status.
## Partial Results
Sometimes not all enrichment modules succeed. For example, a transaction might have a recognized merchant but no identifiable location. In these cases:
* The response includes `partial: true`
* Successful enrichments are returned normally in the `entities` array
* The `meta.errors` array lists which enrichers failed
* No credits are deducted for partial results
```json theme={null}
{
"success": true,
"partial": true,
"data": {
"transaction": { "confidence": { "value": 75, "reasons": [] } },
"entities": [
{
"type": "merchant",
"role": "organization",
"confidence": { "value": 95, "reasons": ["name_closely_matched"] },
"data": { "name": "Acme Corp" }
}
]
},
"meta": {
"errors": ["location_timeout"]
}
}
```
## Best Practices
The country code helps Triqai narrow down merchant and location matching. An
incorrect country code may lead to less accurate results.
Don't pre-process or truncate transaction titles. The full string often
contains valuable signals like store numbers and location hints.
The `type` field (`expense` vs `income`) helps classify refunds, transfers,
and income sources correctly.
Check confidence scores and their reason tags before displaying data to
users. Low-confidence results may need manual review. Reason tags explain
*why* the score is what it is.
## Next Steps
Learn about the category taxonomy
Understand merchant, location, intermediary, and person data
How to interpret confidence scores and reason tags
See the full API documentation
# Entities
Source: https://docs.triqai.com/concepts/entities
Understanding merchants, locations, intermediaries, and persons in the entities array
Triqai enriches transactions with an **entities array** containing real-world entities relevant to understanding the transaction. Each entity has a `type`, `role`, `confidence` (with reason tags), and type-specific `data`.
Only identified entities are included in the array if no location was found, there is simply no location entity present.
## Entity Types
The business or company behind the transaction
The physical place where the transaction occurred
Payment processors, delivery platforms, wallets, and P2P services
Recipients in peer-to-peer transfers
## Entity Structure
Every entity in the `entities` array follows the same shape:
```json theme={null}
{
"type": "merchant",
"role": "organization",
"confidence": {
"value": 98,
"reasons": ["name_closely_matched", "results_consensus"]
},
"data": {
/* type-specific fields */
}
}
```
| Field | Type | Description |
| ------------ | ------ | ------------------------------------------------------------------------------ |
| `type` | string | Entity type: `merchant`, `location`, `intermediary`, or `person` |
| `role` | string | Contextual role (depends on type — see below) |
| `confidence` | object | `{ value: 0-100, reasons: string[] }` — confidence score with explanatory tags |
| `data` | object | Type-specific data fields |
### Entity Roles
Each entity type has specific roles that describe its function in the transaction context:
| Type | Possible Roles | Description |
| -------------- | ------------------------------------------------------ | --------------------------------- |
| `merchant` | `organization`, `financial_institution`, `institution` | The kind of business |
| `location` | `store_location`, `headquarters`, `office` | What the location represents |
| `intermediary` | `processor`, `platform`, `wallet`, `p2p` | The intermediary's function |
| `person` | `recipient` | The person's role in the transfer |
## Merchants
Merchants are the primary entities in most transactions. When identified, you get:
```json theme={null}
{
"type": "merchant",
"role": "organization",
"confidence": {
"value": 98,
"reasons": ["name_closely_matched", "results_consensus"]
},
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Starbucks",
"alias": ["Starbucks Coffee", "SBUX"],
"keywords": ["coffee", "cafe", "drinks"],
"icon": "https://logos.triqai.com/images/starbuckscom",
"description": "Multinational chain of coffeehouses",
"color": "#00704A",
"website": "https://www.starbucks.com",
"domain": "starbucks.com"
}
}
```
### Merchant Fields
| Field | Type | Description |
| ------------- | -------------- | ----------------------------------- |
| `id` | string | Unique identifier for the merchant |
| `name` | string | Canonical merchant name |
| `alias` | string\[] | Alternative names and abbreviations |
| `keywords` | string\[] | Related search terms |
| `icon` | URL \| null | Logo image URL |
| `description` | string | Brief description of the business |
| `color` | string \| null | Brand color (hex format) |
| `website` | URL \| null | Official website |
| `domain` | string \| null | Primary domain name |
### Merchant Coverage
Triqai maintains a database of over **150 million companies** worldwide, with:
* **143,000+ logos** for visual branding
* Normalized names for consistent identification
* Multiple aliases to match various transaction formats
## Locations
Location enrichment provides geographic context for transactions:
```json theme={null}
{
"type": "location",
"role": "store_location",
"confidence": {
"value": 92,
"reasons": ["city_match", "address_closely_matched"]
},
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Starbucks - Times Square",
"formatted": "1530 Broadway, New York, NY 10036, USA",
"phoneNumber": "+1-212-555-0123",
"website": "https://www.starbucks.com/store-locator/store/8289",
"priceRange": "$$",
"rating": { "average": 4.2, "count": 1250, "source": "google" },
"structured": {
"street": "1530 Broadway",
"city": "New York",
"state": "NY",
"postalCode": "10036",
"country": "US",
"countryName": "United States",
"coordinates": {
"latitude": 40.758,
"longitude": -73.9855
},
"timezone": "America/New_York"
}
}
}
```
### Location Fields
| Field | Type | Description |
| ------------------------ | -------------- | --------------------------------------------------- |
| `id` | string | Unique identifier for the location |
| `name` | string | Location name (may include store number) |
| `formatted` | string | Full formatted address |
| `phoneNumber` | string \| null | Contact phone number |
| `website` | URL \| null | Location-specific website |
| `priceRange` | string \| null | Price range indicator (e.g. "\$", "\$\$", "\$\$\$") |
| `rating` | object \| null | Rating with `average`, `count`, and `source` |
| `structured.street` | string | Street address |
| `structured.city` | string | City name |
| `structured.state` | string | State/province/region |
| `structured.postalCode` | string | Postal or ZIP code |
| `structured.country` | string | ISO country code |
| `structured.countryName` | string | Full country name |
| `structured.coordinates` | object | Latitude and longitude |
| `structured.timezone` | string | IANA timezone identifier |
### Location Coverage
* **10M+ places** globally
* **150+ countries** supported
* Store-level precision when available
## Intermediaries
Intermediaries are a unified entity type that replaces the previous separate "payment processor" and "P2P platform" concepts. They represent any service that sits between the customer and the final recipient of funds.
### Intermediary Roles
| Role | Description | Examples |
| ----------- | ----------------------------- | ------------------------------- |
| `processor` | Payment processor/gateway | Stripe, Adyen, Square, Worldpay |
| `platform` | Delivery/marketplace platform | DoorDash, Uber Eats, Instacart |
| `wallet` | Digital wallet/payment app | Apple Pay, Google Pay, Alipay |
| `p2p` | Peer-to-peer transfer service | Venmo, Zelle, Cash App, PayPal |
```json theme={null}
{
"type": "intermediary",
"role": "processor",
"confidence": { "value": 99, "reasons": ["known_processor_match"] },
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"name": "Stripe",
"icon": "https://logos.triqai.com/images/stripecom",
"description": "Online payment processing platform",
"color": "#635BFF",
"website": "https://stripe.com",
"domain": "stripe.com"
}
}
```
### Intermediary Fields
| Field | Type | Description |
| ------------- | -------------- | ----------------------- |
| `id` | string | Unique identifier |
| `name` | string | Intermediary name |
| `icon` | URL \| null | Logo image URL |
| `description` | string \| null | Brief description |
| `color` | string \| null | Brand color (hex) |
| `website` | URL \| null | Official website |
| `domain` | string \| null | Domain without protocol |
### Why It Matters
Intermediary detection is valuable for:
* **Identifying the actual merchant** behind processor-branded transactions
* **Understanding payment methods** used by customers
* **Fraud detection** by recognizing unusual processor patterns
* **Analytics** on payment method preferences
* **Delivery platform tracking** for food delivery and marketplace transactions
## Persons
Person entities appear in P2P transfer transactions to identify the recipient:
```json theme={null}
{
"type": "person",
"role": "recipient",
"confidence": { "value": 98, "reasons": [] },
"data": {
"displayName": "John Doe"
}
}
```
### Person Fields
| Field | Type | Description |
| ------------- | ------ | ---------------------------------------- |
| `displayName` | string | Recipient's name as shown in transaction |
**Privacy**: Person display names are stored per-organization and never shared
globally. This ensures personal information remains private and
GDPR-compliant.
## P2P Transfer Example
A P2P transfer typically produces both an intermediary entity (the platform) and a person entity (the recipient):
```json theme={null}
{
"entities": [
{
"type": "intermediary",
"role": "p2p",
"confidence": { "value": 98, "reasons": ["known_processor_match"] },
"data": {
"id": "p2p_venmo",
"name": "Venmo",
"icon": "https://logos.triqai.com/images/venmocom",
"description": null,
"color": "#3D95CE",
"website": "https://venmo.com",
"domain": "venmo.com"
}
},
{
"type": "person",
"role": "recipient",
"confidence": { "value": 98, "reasons": [] },
"data": {
"displayName": "John Doe"
}
}
]
}
```
## Fetching Entity Details
You can fetch full entity details by ID:
```typescript Node.js theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
const merchant = await triqai.merchants.get("merchant-uuid");
console.log(merchant.name, merchant.website, merchant.icon);
const location = await triqai.locations.get("location-uuid");
console.log(location.formatted, location.structured.city);
const intermediary = await triqai.intermediaries.get("intermediary-uuid");
console.log(intermediary.name);
```
```bash cURL theme={null}
# Merchant
curl https://api.triqai.com/v1/merchants/{id} -H "X-API-Key: YOUR_API_KEY"
# Location
curl https://api.triqai.com/v1/locations/{id} -H "X-API-Key: YOUR_API_KEY"
# Intermediary
curl https://api.triqai.com/v1/intermediaries/{id} -H "X-API-Key: YOUR_API_KEY"
```
## Entity Sharing
Entities are shared resources:
* **Merchants**, **locations**, and **intermediaries** are shared across all organizations
* This ensures consistent identification and reduces duplication
* Entity IDs are stable and can be used for deduplication
**Exception**: Person display names are scoped to your organization for
privacy.
## Next Steps
Learn how to interpret confidence values and reason tags
Explore the entity lookup endpoints
# Best Practices
Source: https://docs.triqai.com/guides/best-practices
Optimize your Triqai integration for performance and reliability
Follow these best practices to get the most out of the Triqai API while building reliable, efficient integrations.
## Data Quality
### Send Complete Transaction Data
Include the full, original transaction string:
```json theme={null}
{
"title": "POS 4392 STARBUCKS STORE #1234 NEW YORK NY 10001",
"country": "US",
"type": "expense"
}
```
```json theme={null}
{
"title": "Starbucks",
"country": "US",
"type": "expense"
}
```
### Use Accurate Country Codes
The country code significantly affects matching accuracy:
* Use the transaction's origin country, not user's country
* Use ISO 3166-1 alpha-2 codes (US, NL, GB)
* Default to account country if unknown
### Set Correct Transaction Type
The `type` field affects category selection:
| Transaction | Type |
| ------------------------- | --------- |
| Purchases, payments, fees | `expense` |
| Salary, refunds, deposits | `income` |
## Performance Optimization
### Deduplicate Similar Transactions
Group identical transactions before enriching to save credits:
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
interface Transaction {
id: string;
title: string;
country: string;
type: "expense" | "income";
}
async function enrichBatch(transactions: Transaction[]) {
const groups = new Map();
for (const tx of transactions) {
const key = `${tx.title.toUpperCase()}|${tx.country}|${tx.type}`;
if (!groups.has(key)) {
groups.set(key, { representative: tx, all: [] });
}
groups.get(key)!.all.push(tx);
}
const results = new Map();
for (const [, group] of groups) {
const result = await triqai.transactions.enrich({
title: group.representative.title,
country: group.representative.country,
type: group.representative.type,
});
for (const tx of group.all) {
results.set(tx.id, result);
}
}
return results;
}
```
### Rate Limit Management
Rate limits are handled automatically via retries and `Retry-After` header support. You can monitor usage through the debug hook:
```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
onResponse: (info) => {
// info.headers contains X-RateLimit-* values
console.log(`Request completed in ${info.durationMs}ms`);
},
});
```
For full rate limit details on a response, use raw requests:
```typescript theme={null}
const resp = await triqai.rawGet("/v1/categories");
console.log(resp.rateLimitInfo.remaining);
console.log(resp.rateLimitInfo.concurrencyRemaining);
```
## Error Handling
### Built-In Retry Logic
Retries are handled automatically with exponential backoff on transient errors (429, 500, 503, 504). You can customize the behavior:
```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
maxRetries: 5, // increase from default 3
retryDelay: 1000, // base delay in ms
maxRetryDelay: 60_000, // max delay cap
});
```
For POST requests, retries only happen when an `idempotencyKey` is provided:
```typescript theme={null}
const result = await triqai.transactions.enrich(
{ title: "STARBUCKS", country: "US", type: "expense" },
{ idempotencyKey: "unique-key-123" },
);
```
### Handle Partial Results
Don't discard partial results — use available data:
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "SOME TRANSACTION",
country: "US",
type: "expense",
});
const { entities } = result.data;
const merchant = entities.find(e => e.type === "merchant");
const location = entities.find(e => e.type === "location");
const processed = {
category: result.data.transaction.category,
merchant: merchant?.data ?? null,
location: location?.data ?? null,
};
if (result.partial) {
console.warn("Partial result — some enrichers failed");
}
```
## Security
### Protect API Keys
* Store keys in environment variables
* Never commit keys to version control
* Use separate keys for dev/staging/prod
* Rotate keys periodically
```typescript theme={null}
import Triqai from "triqai";
// Good: Environment variable
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
// Bad: Hardcoded — never do this!
// const triqai = new Triqai("triq_abc123...");
```
### Make Requests Server-Side
Never expose your API key in client-side code:
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
app.post("/api/enrich", async (req, res) => {
const result = await triqai.transactions.enrich({
title: req.body.title,
country: req.body.country,
type: req.body.type,
});
res.json(result);
});
```
## Monitoring and Observability
### Track Key Metrics
Monitor these metrics for your integration:
| Metric | Why It Matters |
| --------------------------- | ----------------------------- |
| **Success rate** | Detect issues early |
| **Latency (p50, p95, p99)** | Identify performance problems |
| **Partial result rate** | Track data quality |
| **Error rate by code** | Understand failure patterns |
| **Credit consumption** | Manage costs |
### Structured Logging
Use the debug hooks for structured logging:
```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
onResponse: (info) => {
console.log(JSON.stringify({
event: "triqai_request",
timestamp: new Date().toISOString(),
status: info.status,
durationMs: info.durationMs,
}));
},
});
```
## Architecture Patterns
### Async Processing for Bulk Data
For large volumes, process asynchronously:
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
// Producer: Queue transactions
async function queueTransactions(transactions: Array<{ id: string; title: string; country: string; type: "expense" | "income" }>) {
for (const tx of transactions) {
await messageQueue.send({ type: "enrich", payload: tx });
}
}
// Consumer: Process from queue
async function processQueue() {
while (true) {
const message = await messageQueue.receive();
const result = await triqai.transactions.enrich(message.payload);
await saveResult(message.payload.id, result);
}
}
```
### Graceful Degradation
Design for partial failures:
```typescript theme={null}
async function getTransactionDisplay(tx: { title: string; country: string; type: "expense" | "income" }) {
try {
const enrichment = await triqai.transactions.enrich(tx);
return formatEnrichedTransaction(tx, enrichment);
} catch (error) {
console.error("Enrichment failed:", error);
return formatRawTransaction(tx);
}
}
```
## Checklist
Use this checklist when building your integration:
* API key stored securely in environment
* Full transaction strings sent, not truncated
* Caching implemented for duplicate transactions
* Retry logic with exponential backoff
* Rate limiting handled gracefully
* Partial results processed correctly
* Errors logged with requestId
* Metrics and monitoring in place
* Credit usage tracked
## Next Steps
Comprehensive error handling guide
Manage request limits effectively
# Enriching Transactions
Source: https://docs.triqai.com/guides/enriching-transactions
A complete guide to enriching transactions with Triqai
This guide walks through the complete process of enriching transactions, from preparing your data to processing the results.
## Preparing Transaction Data
### Required Fields
Every enrichment request needs three fields:
```json theme={null}
{
"title": "AMAZON MKTPLACE PMTS AMZN.COM/BILL WA",
"country": "US",
"type": "expense"
}
```
| Field | Type | Description |
| --------- | ------ | ---------------------------------------------- |
| `title` | string | Raw transaction description (1-256 characters) |
| `country` | string | ISO 3166-1 alpha-2 country code |
| `type` | string | `expense` or `income` |
### Transaction Title Best Practices
Don't truncate or pre-process the transaction title. Include everything from
the bank statement: `✓ "POS 4392 STARBUCKS STORE #1234 NEW YORK NY 10001"
✗ "STARBUCKS"` The full string contains valuable signals (store numbers,
locations, dates) that improve accuracy.
Keep the original case, spacing, and punctuation: `✓
"AMAZON.COM*2K4X9T3H2 AMZN.COM/BILL WA" ✗ "Amazon"`
If your transaction source provides additional description fields,
concatenate them: `javascript const title = [ transaction.description,
transaction.merchantName, transaction.locationInfo ].filter(Boolean).join('
'); `
### Country Code
The country code should be:
* ISO 3166-1 alpha-2 format (2 letters)
* The country where the transaction originated
* Uppercase or lowercase (both work)
```javascript theme={null}
// Valid country codes
"US"; // United States
"NL"; // Netherlands
"GB"; // United Kingdom
"DE"; // Germany
"FR"; // France
```
If you're unsure of the country, use the account holder's primary country.
Transaction strings often contain location hints that Triqai can use for more
accurate matching.
### Transaction Type
Set `type` based on the transaction direction:
| Type | When to Use |
| --------- | ---------------------------------------------------------- |
| `expense` | Money leaving the account (purchases, payments, fees) |
| `income` | Money entering the account (salary, refunds, transfers in) |
This affects how Triqai interprets the transaction and which categories it considers.
## Making the Request
### Basic Request
```typescript Node.js theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
const result = await triqai.transactions.enrich({
title: "NETFLIX.COM",
country: "US",
type: "expense",
});
```
```bash cURL theme={null}
curl -X POST https://api.triqai.com/v1/transactions/enrich \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"title": "NETFLIX.COM",
"country": "US",
"type": "expense"
}'
```
```python Python theme={null}
import requests
import os
response = requests.post(
'https://api.triqai.com/v1/transactions/enrich',
headers={
'Content-Type': 'application/json',
'X-API-Key': os.environ['TRIQAI_API_KEY']
},
json={
'title': 'NETFLIX.COM',
'country': 'US',
'type': 'expense'
}
)
result = response.json()
```
### Processing Multiple Transactions
For multiple transactions, make individual requests. Retries and rate limits are handled automatically:
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
async function enrichTransactions(
transactions: Array<{ description: string; country: string; amount: number }>,
) {
const results = [];
for (const tx of transactions) {
const result = await triqai.transactions.enrich({
title: tx.description,
country: tx.country,
type: tx.amount < 0 ? "expense" : "income",
});
results.push(result);
}
return results;
}
```
Transient errors (429, 500, 503) are automatically retried with exponential
backoff, so you don't need to implement retry or rate-limit logic yourself.
## Processing the Response
### Successful Response
A successful enrichment returns structured data with an entities array:
```json theme={null}
{
"success": true,
"partial": false,
"data": {
"transaction": {
"category": {
"primary": {
"name": "Entertainment",
"code": { "mcc": 4899, "sic": 7841, "naics": 532230 }
},
"secondary": {
"name": "Streaming Services",
"code": { "mcc": 4899, "sic": 7841, "naics": 532230 }
},
"tertiary": null,
"confidence": { "value": 98, "reasons": ["merchant_category_match"] }
},
"confidence": { "value": 96, "reasons": [] }
},
"entities": [
{
"type": "merchant",
"role": "organization",
"confidence": {
"value": 99,
"reasons": ["name_closely_matched", "results_consensus"]
},
"data": {
"id": "...",
"name": "Netflix",
"icon": "https://logos.triqai.com/images/netflixcom",
"website": "https://www.netflix.com"
}
}
]
},
"meta": {
"generatedAt": "2026-01-19T10:30:00Z",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a123",
"version": "1.3.13",
"categoryVersion": "triqai-2026.01"
}
}
```
### Extracting Key Data
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
const result = await triqai.transactions.enrich({
title: "STARBUCKS STORE #1234 NEW YORK NY",
country: "US",
type: "expense",
});
const { data } = result;
const { entities } = data;
const findEntity = (type: string) => entities.find((e) => e.type === type);
const merchant = findEntity("merchant");
const location = findEntity("location");
const intermediary = findEntity("intermediary");
const processed = {
category: data.transaction.category.primary.name,
subcategory: data.transaction.category.secondary?.name,
confidence: data.transaction.confidence.value,
merchantName: merchant?.data.name,
merchantLogo: merchant?.data.icon,
location: location?.data.formatted,
intermediary: intermediary?.data.name,
intermediaryRole: intermediary?.role,
};
```
## Handling Different Transaction Types
### Regular Purchases
Most transactions identify a merchant directly:
```json theme={null}
{ "title": "STARBUCKS STORE 1234", "country": "US", "type": "expense" }
```
### Intermediary Transactions
When a payment processor or platform is involved:
```json theme={null}
{ "title": "STRIPE* ACME INC", "country": "US", "type": "expense" }
```
Triqai identifies the intermediary (Stripe, role `processor`) and attempts to identify the underlying merchant (Acme Inc).
### P2P Transfers
Peer-to-peer payments return both an intermediary and a person entity:
```json theme={null}
{ "title": "VENMO PAYMENT TO JOHN DOE", "country": "US", "type": "expense" }
```
Returns the P2P platform as an intermediary (role `p2p`) and the recipient as a person entity.
### Income Transactions
Refunds, salary, and other income:
```json theme={null}
{ "title": "PAYROLL ACME CORP", "country": "US", "type": "income" }
```
Uses income-specific categories and classification logic.
## Next Steps
Learn to process different response types
Handle errors and edge cases
See the full API documentation
Optimization tips and patterns
# Error Handling
Source: https://docs.triqai.com/guides/error-handling
Handle API errors gracefully in your application
This guide covers how to handle errors from the Triqai API, including common error codes, troubleshooting tips, and best practices.
## Error Response Format
All error responses follow a consistent structure:
```json theme={null}
{
"success": false,
"error": {
"code": "error_code",
"message": "Human-readable description",
"details": {
/* optional additional info */
}
},
"meta": {
"generatedAt": "2026-01-19T10:30:00Z",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a123",
"version": "1.3.13"
}
}
```
## Error Codes
### Authentication Errors (401)
| Code | Message | Solution |
| ---------------------- | -------------------------- | ------------------------------ |
| `authentication_error` | Invalid or missing API key | Check your `X-API-Key` header |
| `authentication_error` | Invalid API key format | Ensure key starts with `triq_` |
```typescript theme={null}
import { AuthenticationError } from "triqai";
try {
await triqai.transactions.enrich({
title: "TEST",
country: "US",
type: "expense",
});
} catch (err) {
if (err instanceof AuthenticationError) {
console.error("API key invalid. Please check your configuration.");
}
}
```
### Payment Errors (402)
| Code | Message | Solution |
| ---------------------- | -------------------- | ------------------------------- |
| `insufficient_credits` | Insufficient credits | Enable overages or upgrade plan |
```typescript theme={null}
import { InsufficientCreditsError } from "triqai";
try {
await triqai.transactions.enrich({
title: "TEST",
country: "US",
type: "expense",
});
} catch (err) {
if (err instanceof InsufficientCreditsError) {
console.log("Top up credits at https://triqai.com/dashboard");
}
}
```
### Not Found Errors (404)
| Code | Message | Solution |
| ----------- | ------------------ | -------------------- |
| `not_found` | Resource not found | Verify the ID exists |
```typescript theme={null}
import { NotFoundError } from "triqai";
try {
await triqai.transactions.get("nonexistent-id");
} catch (err) {
if (err instanceof NotFoundError) {
console.error("Resource not found");
}
}
```
### Validation Errors (422)
| Code | Message | Details |
| ------------------ | ----------------- | ------------------------------------------ |
| `validation_error` | Validation failed | `fieldErrors` object with per-field errors |
```json theme={null}
{
"error": {
"code": "validation_error",
"message": "Validation failed",
"details": {
"fieldErrors": {
"title": ["Title is required"],
"country": ["Invalid country code. Use ISO 3166-1 alpha-2 format."],
"type": ["Type must be 'expense' or 'income'"]
}
}
}
}
```
```typescript theme={null}
import { ValidationError } from "triqai";
try {
await triqai.transactions.enrich({
title: "",
country: "INVALID",
type: "expense",
});
} catch (err) {
if (err instanceof ValidationError) {
console.log("Field errors:", err.fieldErrors);
// { title: ["Title is required"], country: ["Invalid country code"] }
}
}
```
### Rate Limit Errors (429)
| Code | Message | Headers |
| -------------- | ------------------- | ------------------------------ |
| `rate_limited` | Rate limit exceeded | `Retry-After`, `X-RateLimit-*` |
```typescript theme={null}
import { RateLimitError } from "triqai";
try {
await triqai.transactions.enrich({
title: "TEST",
country: "US",
type: "expense",
});
} catch (err) {
if (err instanceof RateLimitError) {
console.log(`Rate limited. Retry after ${err.rateLimitInfo.retryAfter}s`);
}
}
```
Rate-limited requests are automatically retried with exponential backoff, so
you typically don't need to handle 429 errors manually.
### Server Errors (500)
| Code | Message | Solution |
| ---------------- | ---------------------------- | ------------------------------ |
| `internal_error` | An unexpected error occurred | Retry with exponential backoff |
```typescript theme={null}
import { InternalServerError } from "triqai";
// 500 errors are retried automatically (up to 3 times by default).
// You can customize this behavior:
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
maxRetries: 5,
retryDelay: 1000,
});
```
## Automatic Retries
Retries are handled automatically with exponential backoff. You can customize the retry behavior:
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
maxRetries: 3, // max retry attempts (default: 3)
retryDelay: 500, // base delay in ms (default: 500)
maxRetryDelay: 30_000, // max delay cap (default: 30000)
});
```
**Retry behavior:**
* `GET` and `DELETE` requests are always retried on transient errors
* `POST` requests are only retried when an `idempotencyKey` is provided
* The `Retry-After` header from 429 responses is respected automatically
* Retried status codes: 429, 500, 503, 504, and network errors
To disable retries entirely:
```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, { maxRetries: 0 });
```
## Complete Error Handling Pattern
Here's a complete error handling example using typed error classes:
```typescript theme={null}
import Triqai, {
TriqaiError,
AuthenticationError,
ValidationError,
RateLimitError,
InsufficientCreditsError,
NotFoundError,
} from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
try {
const result = await triqai.transactions.enrich({
title: "STARBUCKS NYC",
country: "US",
type: "expense",
});
} catch (err) {
if (err instanceof AuthenticationError) {
console.error("Invalid API key — check your configuration");
} else if (err instanceof ValidationError) {
console.log("Validation errors:", err.fieldErrors);
} else if (err instanceof RateLimitError) {
console.log(`Rate limited — retry after ${err.rateLimitInfo.retryAfter}s`);
} else if (err instanceof InsufficientCreditsError) {
console.log("No credits remaining — top up at 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);
}
}
```
See the [error handling reference](/sdk/error-handling) for the full list of error classes.
## Logging and Monitoring
### Request Logging
Use built-in debug hooks for observability:
```typescript theme={null}
const triqai = new Triqai(process.env.TRIQAI_API_KEY!, {
onRequest: (info) => {
console.log(`→ ${info.method} ${info.url}`);
},
onResponse: (info) => {
console.log(`← ${info.status} in ${info.durationMs}ms`);
},
});
```
Or wrap calls for custom logging:
```typescript theme={null}
async function enrichWithLogging(transaction: {
title: string;
country: string;
type: string;
}) {
const startTime = Date.now();
try {
const result = await triqai.transactions.enrich(transaction);
console.log({
event: "enrichment_success",
duration: Date.now() - startTime,
});
return result;
} catch (err) {
if (err instanceof TriqaiError) {
console.error({
event: "enrichment_error",
code: err.code,
requestId: err.requestId,
});
}
throw err;
}
}
```
### Error Tracking
```typescript theme={null}
import { TriqaiError } from "triqai";
function trackError(error: TriqaiError, context: Record = {}) {
errorTracker.captureException(error, {
tags: {
service: "triqai",
errorCode: error.code,
},
extra: {
requestId: error.requestId,
statusCode: error.statusCode,
...context,
},
});
}
```
## Best Practices
The `meta.requestId` helps with debugging and support requests.
If errors persist, temporarily stop requests to prevent cascading failures.
Partial results (`partial: true`) are successes with some failures handle
them differently from full failures.
Catch validation errors client-side when possible to reduce failed API
calls.
Create error classes that make it easy to check error types and extract
details.
## Next Steps
Complete API documentation with error codes
Understand and manage rate limits
# Handling Responses
Source: https://docs.triqai.com/guides/handling-responses
Process enrichment responses correctly in your application
Triqai API responses follow a consistent structure. This guide explains how to handle different response scenarios effectively.
## Response Structure
All responses share a common structure:
```json theme={null}
{
"success": boolean,
"partial": boolean, // Only for enrichment
"data": { ... }, // On success
"error": { ... }, // On failure
"meta": {
"generatedAt": "ISO timestamp",
"requestId": "unique ID",
"version": "API version"
}
}
```
## Success Responses
### Full Success
When all enrichment modules succeed, the `entities` array contains all identified entities:
```json theme={null}
{
"success": true,
"partial": false,
"data": {
"transaction": {
"category": { "primary": { "name": "Coffee Shops" }, "confidence": { "value": 95, "reasons": ["merchant_category_match"] } },
"confidence": { "value": 92, "reasons": [] }
},
"entities": [
{ "type": "merchant", "role": "organization", "confidence": { "value": 98, "reasons": ["name_closely_matched"] }, "data": { "name": "Starbucks" } },
{ "type": "location", "role": "store_location", "confidence": { "value": 85, "reasons": ["city_match"] }, "data": { "formatted": "1530 Broadway, New York" } }
]
}
}
```
**Processing:**
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
const result = await triqai.transactions.enrich({
title: "STARBUCKS STORE 1234 NEW YORK",
country: "US",
type: "expense",
});
const { entities } = result.data;
const merchant = entities.find(e => e.type === "merchant");
const location = entities.find(e => e.type === "location");
const intermediary = entities.find(e => e.type === "intermediary");
const person = entities.find(e => e.type === "person");
```
### Partial Success
When some enrichment modules succeed and others fail:
```json theme={null}
{
"success": true,
"partial": true,
"data": {
"transaction": {
"confidence": { "value": 75, "reasons": [] }
},
"entities": [
{
"type": "merchant",
"role": "organization",
"confidence": { "value": 95, "reasons": ["name_closely_matched"] },
"data": { "name": "Acme Corp" }
}
]
},
"meta": {
"errors": ["location_timeout"]
}
}
```
**Processing:**
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "UNKNOWN STORE 99999",
country: "US",
type: "expense",
});
if (result.partial) {
const merchant = result.data.entities.find(e => e.type === "merchant");
if (merchant) {
console.log("Merchant found:", merchant.data.name);
}
}
```
## Working with the Entities Array
The entities array only contains identified entities. To check for a specific entity type, use `find`:
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "STARBUCKS STORE 1234 NEW YORK",
country: "US",
type: "expense",
});
const { entities } = result.data;
const getEntity = (type: string) => entities.find(e => e.type === type) ?? null;
const processed = {
hasMerchant: !!getEntity("merchant"),
hasLocation: !!getEntity("location"),
hasIntermediary: !!getEntity("intermediary"),
hasPerson: !!getEntity("person"),
merchant: getEntity("merchant")?.data,
location: getEntity("location")?.data,
intermediary: getEntity("intermediary")?.data,
person: getEntity("person")?.data,
};
```
## Error Responses
When a request fails entirely:
```json theme={null}
{
"success": false,
"error": {
"code": "validation_error",
"message": "Validation failed",
"details": {
"fieldErrors": {
"title": ["Title is required"],
"country": ["Invalid country code"]
}
}
},
"meta": { }
}
```
### Error Response Handling
Errors are thrown as typed exceptions you can catch directly:
```typescript theme={null}
import Triqai, { ValidationError, RateLimitError, TriqaiError } from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
try {
const result = await triqai.transactions.enrich({
title: "",
country: "US",
type: "expense",
});
} catch (err) {
if (err instanceof ValidationError) {
console.log("Field errors:", err.fieldErrors);
} else if (err instanceof RateLimitError) {
console.log("Retry after:", err.rateLimitInfo.retryAfter, "seconds");
} else if (err instanceof TriqaiError) {
console.log(`API error ${err.statusCode}: ${err.message}`);
}
}
```
## Working with Enrichment Data
### Merchant Data
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "STARBUCKS NYC",
country: "US",
type: "expense",
});
const merchant = result.data.entities.find(e => e.type === "merchant");
if (merchant) {
console.log(merchant.data.name); // "Starbucks"
console.log(merchant.data.icon); // logo URL
console.log(merchant.data.website); // "https://www.starbucks.com"
console.log(merchant.confidence.value); // 98
}
```
### Location Data
```typescript theme={null}
const location = result.data.entities.find(e => e.type === "location");
if (location) {
console.log(location.data.formatted); // "1530 Broadway, New York"
console.log(location.data.structured.city); // "New York"
console.log(location.data.structured.coordinates); // { latitude, longitude }
console.log(location.confidence.value); // 85
}
```
### Category Data
```typescript theme={null}
const { category } = result.data.transaction;
const parts = [
category.primary.name,
category.secondary?.name,
category.tertiary?.name,
].filter(Boolean);
console.log(parts.join(" > ")); // "Coffee Shops > Food & Dining"
console.log(category.primary.code.mcc); // 5814
console.log(category.confidence.value); // 95
```
## Complete Processing Example
```typescript theme={null}
import Triqai, { TriqaiError } from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
async function processTransaction(title: string, country: string, type: "expense" | "income") {
try {
const result = await triqai.transactions.enrich({ title, country, type });
const { transaction, entities } = result.data;
const find = (t: string) => entities.find(e => e.type === t);
const merchant = find("merchant");
const location = find("location");
const intermediary = find("intermediary");
const person = find("person");
return {
category: transaction.category.primary.name,
subcategory: transaction.category.secondary?.name,
confidence: transaction.confidence.value,
merchant: merchant ? {
name: merchant.data.name,
logo: merchant.data.icon,
confidence: merchant.confidence.value,
} : null,
location: location ? {
formatted: location.data.formatted,
coordinates: location.data.structured.coordinates,
confidence: location.confidence.value,
} : null,
intermediary: intermediary ? {
name: intermediary.data.name,
role: intermediary.role,
confidence: intermediary.confidence.value,
} : null,
person: person ? {
displayName: person.data.displayName,
confidence: person.confidence.value,
} : null,
};
} catch (err) {
if (err instanceof TriqaiError) {
console.error(`API error ${err.statusCode}: ${err.message}`);
}
throw err;
}
}
```
## Best Practices
Before accessing data, verify `success === true`.
Don't fail if some entities are missing. Use what's available in the entities array.
Always use `entities.find(e => e.type === 'merchant')` and check for `null` before accessing `.data`.
Show confidence indicators to users or flag low-confidence results. Use reason tags for smarter decisions.
Keep the `meta.requestId` for debugging and issue reports.
## Next Steps
Handle errors and edge cases
Interpret confidence values and reason tags
# Triqai API Documentation
Source: https://docs.triqai.com/index
Official documentation for Triqai's API platform to enrich raw bank transactions with merchant data, categories, locations, and structured financial intelligence.
Triqai is a developer-first API platform that enriches raw bank transaction data into structured, reliable, and globally usable information.
Banks, fintechs, accounting tools, and personal finance apps often receive transactions as noisy, inconsistent strings like `POS 4392 STRIPE*AMAZON EU`. Triqai transforms these into clean, machine-readable objects that are easy to understand, analyze, and build products on.
Test Triqai instantly, no API key required. See enrichment quality before you sign up.
## Why Triqai?
Transaction data from banks is messy. It contains cryptic abbreviations, inconsistent formatting, and missing context. This makes it nearly impossible to build reliable financial products without significant manual effort.
Triqai solves this by:
* **Identifying merchants** from cryptic transaction strings
* **Extracting locations** even from partial address fragments
* **Categorizing spending** with consistent, hierarchical categories
* **Detecting intermediaries** like payment processors, delivery platforms, wallets, and P2P services
* **Identifying P2P recipients** for person-to-person transfers
* **Providing confidence scores with reason tags** so you know when to trust the data and why
## Core Capabilities
Normalized merchant names, logos, websites, and brand metadata
Hierarchical spending categories with MCC, SIC, and NAICS codes
Store-level addresses, coordinates, and timezone data
## What You Get
Every enrichment request returns structured data including:
| Field | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------- |
| **Merchant** | Normalized name, logo, website, domain, and brand color |
| **Location** | Formatted address, city, state, country, coordinates, timezone, and rating |
| **Category** | Primary, secondary, and tertiary categories with industry codes |
| **Intermediary** | Detection of processors, platforms, wallets, and P2P services (Stripe, DoorDash, Apple Pay, Venmo, etc.) |
| **Person** | P2P transfer recipient identification |
| **Channel** | Transaction method: online, in-store, mobile app, ATM, or bank transfer |
| **Confidence** | Per-field confidence scores (0-100) with explanatory reason tags |
## Use Cases
Triqai is built for a wide range of financial data applications:
Display clean transaction lists with merchant logos and spending insights. Help users understand where their money goes with accurate categorization.
Auto-categorize transactions for bookkeeping and reconciliation. Reduce manual data entry with normalized merchant data.
Standardize transaction data across banks and regions for consistent, trustworthy experiences. Power PFM features in banking apps.
Flag anomalies using location data and transaction metadata. Detect unusual patterns with enriched context.
Build dashboards with normalized, consistent transaction data. Aggregate spending by merchant, category, or location.
## Global Coverage
Triqai works across countries, languages, and currencies with best accuracy in:
* **Europe**: Netherlands, UK, Germany, France, and more
* **North America**: United States and Canada
* **Growing coverage**: Support for all countries with continuously improving accuracy
## Getting Started
Get your API key and make your first enrichment request in minutes
Install the official SDK for type-safe access to every endpoint
Explore the complete API documentation with examples
Test the API without signing up
## Need Help?
Reach out to our team for technical assistance
Schedule a call to discuss your use case
# Brand Data
Source: https://docs.triqai.com/platform/brand-data
Guidance on fair use of logos and brand data returned by the enrichment endpoint
Triqai provides brand data through the enrichment endpoint (`POST /v1/transactions/enrich`) to help you build better end-user experiences.
This page provides product guidance, not legal advice. You are responsible for ensuring your use complies with applicable laws, regulations, and third-party terms.
## Data Source Notice
Triqai may source parts of its brand dataset from third-party providers, including Brand.dev. You remain responsible for using returned data in accordance with applicable laws, regulations, and third-party terms.
## Fair Use Guidance
When using brand data from Triqai:
* Use it to improve user understanding of transactions and merchant identity
* Follow applicable trademark, copyright, consumer protection, and platform rules
* Keep representations accurate and avoid confusing end users
## Responsible Usage Requirements
You should **not**:
* Imply that a brand endorses your product or service when it does not
* Alter a brand's logo or assets without permission
* Use logos or brand metadata in misleading or deceptive ways
You should:
* Present logos and brand attributes in clear transaction context
* Preserve brand identity as returned by the API
* Validate that your implementation meets your legal and compliance requirements
## Logo Resolution Behavior
For logo URLs returned by the enrichment endpoint (`/enrich`):
* A lower-resolution logo can be returned first
* A higher-resolution version is typically available after about **1 to 5 seconds**
* Final logo quality can be up to **256px**
For the best UX, render immediately and refresh the logo shortly after initial load.
## Storage and Caching Permissions
You are allowed to store enrichment data on your own infrastructure, including:
* Logos returned by enrichment responses
* All other fields returned by the enrichment endpoint
You may cache and serve this data from your own systems to improve performance and reliability.
## Next Steps
Learn implementation patterns for processing enrichment output
Improve reliability and performance in production
# Credits
Source: https://docs.triqai.com/platform/credits
Understanding credit consumption, billing, and overages
Triqai uses a credit-based billing model. Each successful enrichment consumes credits from your monthly allocation.
## How Credits Work
* **1 credit = 1 enrichment**: Each successfully enriched transaction consumes 1 credit
* **Monthly allocation**: Credits are included in your plan and reset each billing cycle
* **No rollover**: Unused credits don't carry over to the next month
* **Overages available**: Continue enriching beyond your allocation with per-request billing
## Credits by Plan
| Plan | Monthly Credits | Overage Cost (per 1K) |
| -------------- | --------------- | --------------------- |
| **Free** | 100 | Not available |
| **Starter** | 4,000 | €5 |
| **Growth** | 25,000 | €4 |
| **Business** | 100,000 | €3 |
| **Enterprise** | Custom | Custom |
Overage pricing is per 1,000 additional enrichments beyond your plan's
included credits.
## When Credits Are Deducted
Credits are consumed when:
An enrichment request completes fully (`success: true`, `partial: false`)
Credits are **not** deducted when:
* The enrichment returns a partial result (`partial: true`)
* The request fails entirely (validation error, auth error, server error)
* You hit your credit limit and overages are disabled
* You query existing transactions (GET requests)
* You fetch categories or entity details
## Checking Your Balance
Monitor your credit usage in the [dashboard](https://www.triqai.com/dashboard):
* Current credit balance
* Usage history by day/week/month
* Overage usage (if enabled)
## Overage Billing
When you exceed your monthly credit allocation:
### With Overages Disabled (Default)
* Enrichment requests will fail with `402 Payment Required`
* Error code: `insufficient_credits`
* You'll need to wait for credits to reset or upgrade your plan
```json theme={null}
{
"success": false,
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits. Your monthly allocation is exhausted."
}
}
```
### With Overages Enabled
* Enrichment continues without interruption
* Overage usage is tracked separately
* Billed at the end of your billing cycle
To enable overages:
1. Go to your [dashboard settings](https://www.triqai.com/dashboard)
2. Navigate to Billing > Overage Settings
3. Toggle "Enable Overages"
Free tier accounts cannot enable overages. Upgrade to Starter or higher to
continue enriching beyond your allocation.
## Credit Reset
Credits reset at the start of each billing cycle:
* **Monthly plans**: Reset on your plan anniversary date
* **Annual plans**: Reset monthly on the same day you subscribed
* **Free tier**: Reset monthly on the same day you joined or your old plan expired
## Monitoring Usage
The dashboard provides real-time visibility into:
* Credits remaining this period
* Usage trends over time
* Overage consumption (if applicable)
Display credit usage on dashboard can be delayed by max 5 minutes
## Optimizing Credit Usage
Check if you've already enriched similar transactions. Transactions with the
same raw title in the same country often produce identical results.
The [playground](https://www.triqai.com/playground) is free and doesn't
consume credits. Use it for exploration and testing.
If you have many identical transactions (e.g., recurring payments),
enrich one and apply the result to all.
## Upgrading Your Plan
If you consistently need more credits:
View all plans and included credits
Discuss custom enterprise pricing
## FAQ
One enrichment request that returns a fully successful result (`partial: false`)
consumes one credit. Partial results do not consume credits.
No. Fetching transaction history, categories, or entity details does not
consume credits. Only POST requests to the enrichment endpoint use credits.
Partial results (where some enrichers succeed but others fail) do not consume
credits. Only fully successful enrichments are charged.
Credits are non-refundable but reset monthly. Consider adjusting your plan if
you consistently have excess credits.
Overage usage is calculated at the end of your billing cycle and charged to
your payment method on file.
## Next Steps
Understand request rate limits
View plan details and pricing
# Rate Limits
Source: https://docs.triqai.com/platform/rate-limits
Understanding API rate limits and how to handle them
Triqai applies rate limits to ensure fair usage and maintain service reliability. Rate limits vary by plan and are applied per organization.
## Rate Limits by Plan
| Plan | Requests per Minute (RPM) | Requests per Second |
| -------------- | ------------------------- | ------------------- |
| **Free** | 60 RPM | 1 RPS |
| **Starter** | 300 RPM | 5 RPS |
| **Growth** | 600 RPM | 10 RPS |
| **Business** | 1,200 RPM | 20 RPS |
| **Enterprise** | Custom | Custom |
Rate limits use a token bucket algorithm (requests per second) combined with a
concurrent in-flight request cap per organization.
## Rate Limit Headers
Every API response includes rate limit information in the headers:
| Header | Description |
| ----------------------------------- | ----------------------------------------------- |
| `X-RateLimit-Limit` | Maximum requests allowed per window |
| `X-RateLimit-Remaining` | Requests remaining in current window |
| `X-RateLimit-Reset` | ISO timestamp when the limit resets |
| `X-RateLimit-Scope` | Which limit was applied: `rps` or `concurrency` |
| `X-RateLimit-Concurrency-Limit` | Maximum concurrent in-flight requests allowed |
| `X-RateLimit-Concurrency-Remaining` | Concurrent request slots remaining |
Example response headers:
```
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 2026-01-19T10:30:01.000Z
X-RateLimit-Scope: rps
```
## Rate Limit Exceeded
When you exceed the rate limit, you'll receive a `429 Too Many Requests` response:
```json theme={null}
{
"success": false,
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Maximum 10 requests per second. Retry after 2 seconds."
},
"meta": {
"generatedAt": "2026-01-19T10:30:00Z",
"requestId": "3c90c3cc-0d44-4b50-8888-8dd25736052a123",
"version": "1.3.13"
}
}
```
Additional headers on 429 responses:
| Header | Description |
| ------------- | ------------------------------- |
| `Retry-After` | Seconds to wait before retrying |
## Best Practices
Instead of enriching transactions one at a time in rapid succession, batch
them and process at a controlled rate.
For large batches, consider processing asynchronously rather than blocking
on immediate results.
Track rate limit metrics in your dashboard to understand usage patterns and
plan capacity.
If you're consistently hitting rate limits, consider upgrading your plan for
higher limits.
## Upgrading Rate Limits
If you need higher rate limits:
1. **Upgrade your plan**: Higher tiers include increased limits
2. **Contact sales**: For enterprise needs, we offer custom rate limits
Compare plans and rate limits
## Rate Limits Per Endpoint
All authenticated endpoints share the same rate limit pool. The limits apply to:
* `POST /v1/transactions/enrich`
* `GET /v1/transactions`
* `GET /v1/transactions/{id}`
* `DELETE /v1/transactions/{id}`
* `GET /v1/categories`
* `GET /v1/merchants/{id}`
* `GET /v1/locations/{id}`
* `GET /v1/intermediaries/{id}`
* `POST /v1/report-issue`
Read-only endpoints (GET requests) count toward the same limit as enrichment
requests. Design your application to minimize unnecessary API calls.
## Next Steps
Learn about credit consumption and billing
Handle rate limits and other errors gracefully
# Quickstart
Source: https://docs.triqai.com/quickstart
Get started with Triqai in under 5 minutes
This guide will walk you through making your first transaction enrichment request with the Triqai API.
## Prerequisites
Before you begin, you'll need:
1. A Triqai account ([sign up for free](https://www.triqai.com/register))
2. An API key from your [dashboard](https://www.triqai.com/dashboard)
Don't have an account yet? You can test the API in our
[Playground](https://www.triqai.com/playground) without signing up.
## Step 1: Get Your API Key
Sign up at [triqai.com/register](https://www.triqai.com/register). No credit
card required for the free tier.
Navigate to your [dashboard](https://www.triqai.com/dashboard) after signing
in.
Your API key is displayed in the dashboard. It starts with `triq_`.
Keep your API key secure. Never expose it in client-side code or public
repositories.
## Step 2: Install the SDK (Recommended)
The fastest way to get started is with the official [Node.js / TypeScript SDK](https://www.npmjs.com/package/triqai):
```bash npm theme={null}
npm install triqai
```
```bash yarn theme={null}
yarn add triqai
```
```bash pnpm theme={null}
pnpm add triqai
```
## Step 3: Make Your First Request
The core operation is enriching a transaction. Here's how to use it:
```typescript Node.js theme={null}
import Triqai from "triqai";
const triqai = new Triqai("YOUR_API_KEY");
const result = await triqai.transactions.enrich({
title: "PP* #56789 MK:678321 LELLO, PORTO Ref:hwjk2-23123 PAGAMENTO",
country: "BR",
type: "expense",
});
console.log(result.data.transaction.category.primary.name);
console.log(result.data.entities);
```
```bash cURL theme={null}
curl -X POST https://api.triqai.com/v1/transactions/enrich \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"title": "PP* #56789 MK:678321 LELLO, PORTO Ref:hwjk2-23123 PAGAMENTO",
"country": "BR",
"type": "expense"
}'
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.triqai.com/v1/transactions/enrich',
headers={
'Content-Type': 'application/json',
'X-API-Key': 'YOUR_API_KEY'
},
json={
'title': 'PP* #56789 MK:678321 LELLO, PORTO Ref:hwjk2-23123 PAGAMENTO',
'country': 'BR',
'type': 'expense'
}
)
print(response.json())
```
```go Go theme={null}
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
payload := map[string]string{
"title": "PP* #56789 MK:678321 LELLO, PORTO Ref:hwjk2-23123 PAGAMENTO",
"country": "BR",
"type": "expense",
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.triqai.com/v1/transactions/enrich", bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", "YOUR_API_KEY")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result)
}
```
## Step 4: Understand the Response
A successful enrichment returns comprehensive data about the transaction. The response uses an **entities array**, only identified entities are included:
```json Response theme={null}
{
"success": true,
"partial": false,
"data": {
"transaction": {
"category": {
"primary": {
"name": "Books",
"code": {
"mcc": 5942,
"sic": 5942,
"naics": 451211
}
},
"secondary": {
"name": "Entertainment",
"code": {
"mcc": 7832,
"sic": 7832,
"naics": 713110
}
},
"tertiary": null,
"confidence": { "value": 100, "reasons": ["merchant_category_match"] }
},
"confidence": { "value": 100, "reasons": [] }
},
"entities": [
{
"type": "merchant",
"role": "organization",
"confidence": {
"value": 100,
"reasons": ["name_closely_matched", "results_consensus"]
},
"data": {
"id": "f3c35551-1eb0-460f-a6bf-ce24c64e941b",
"name": "Livraria Lello",
"alias": ["Livraria Mais Bonita do Mundo"],
"keywords": ["books", "literature", "culture"],
"icon": "https://logos.triqai.com/images/livrarialellopt",
"description": "Historic bookstore and cultural institution in Porto, Portugal",
"color": "#002B82",
"website": "https://livrarialello.pt",
"domain": "livrarialello.pt"
}
},
{
"type": "location",
"role": "store_location",
"confidence": {
"value": 80,
"reasons": ["city_match", "single_result_match"]
},
"data": {
"id": "9eda058e-174a-4b9a-8777-4b4d8717c5a3",
"name": "Porto",
"formatted": "R. das Carmelitas 144, 4050-161 Porto",
"phoneNumber": "22 200 2037",
"structured": {
"city": "Porto",
"state": "",
"street": "R. das Carmelitas 144",
"country": "PT",
"timezone": "Europe/Lisbon",
"postalCode": "4050-161",
"coordinates": {
"latitude": 41.1468104,
"longitude": -8.6148718
},
"countryName": "Portugal"
}
}
},
{
"type": "intermediary",
"role": "p2p",
"confidence": { "value": 100, "reasons": ["known_processor_match"] },
"data": {
"id": "b9a3152a-d735-4b8c-8bd2-e525a6b3d903",
"name": "PayPal",
"icon": "https://logos.triqai.com/images/paypalcom",
"description": null,
"color": "#002991",
"website": "https://paypal.com",
"domain": "paypal.com"
}
}
]
},
"meta": {
"generatedAt": "2026-01-14T09:16:09.430Z",
"requestId": "019c1da3-5541-7b4c-b20e-bb03363b3333",
"version": "1.3.13",
"categoryVersion": "triqai-2026.01"
}
}
```
### Key Response Fields
| Field | Description |
| ----------------------------- | ----------------------------------------------------------------------- |
| `success` | Whether the request completed successfully |
| `partial` | `true` if some enrichers failed but others succeeded |
| `data.transaction.category` | Hierarchical category classification with confidence |
| `data.transaction.confidence` | Overall enrichment confidence with reason tags |
| `data.entities[]` | Array of identified entities (merchant, location, intermediary, person) |
| `data.entities[].type` | Entity type: `merchant`, `location`, `intermediary`, or `person` |
| `data.entities[].role` | Entity role in the transaction context |
| `data.entities[].confidence` | Per-entity confidence `{ value, reasons }` |
## Step 5: Try Different Transactions
Test with various transaction types to see how Triqai handles different scenarios:
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "VENMO PAYMENT TO JOHN DOE",
country: "US",
type: "expense",
});
// Returns an intermediary entity (Venmo, role "p2p")
// and a person entity (John Doe, role "recipient")
```
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "STRIPE* ACME CORP",
country: "US",
type: "expense",
});
// Returns an intermediary entity (Stripe, role "processor")
// and a merchant entity (Acme Corp)
```
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "NETFLIX.COM",
country: "US",
type: "expense",
});
// Returns merchant and category classification
// result.data.transaction.category.primary.name
```
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "STARBUCKS STORE 4532 NEW YORK NY",
country: "US",
type: "expense",
});
// Returns a merchant entity and a location entity
// with store-level precision
```
## Next Steps
Now that you've made your first enrichment request, explore these resources:
Use the official SDK for type-safe access with built-in retries and
pagination
Learn about API key management and security best practices
Understand how transaction enrichment works in detail
Explore all available endpoints and parameters
# Advanced Usage
Source: https://docs.triqai.com/sdk/advanced
Automatic retries, rate limiting, raw requests, health checks, and TypeScript types in the Triqai SDK
## Automatic Retries
The SDK automatically retries on transient errors (429, 500, 503, 504, and network errors) with exponential backoff.
**Retry behavior:**
* `GET` and `DELETE` requests are always retried
* `POST` requests are only retried when an `idempotencyKey` is provided
* The `Retry-After` header is respected when present
* Defaults: 3 retries, 500ms base delay, 30s max delay
Disable retries entirely:
```typescript theme={null}
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:
```typescript theme={null}
// Via the debug hook
const triqai = new Triqai("triq_your_api_key", {
onResponse: (info) => {
// info.headers contains X-RateLimit-* values
},
});
// Via raw requests
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 left
```
## Health & API Info
```typescript theme={null}
const health = await triqai.health();
console.log(health.data.status); // "healthy"
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):
```typescript theme={null}
import type { HttpResponse } from "triqai";
const resp: HttpResponse = await triqai.rawGet("/v1/categories");
console.log(resp.data); // response body
console.log(resp.rateLimitInfo); // { limit, remaining, ... }
```
The client also exposes `rawPost` and `rawDelete` for other HTTP methods.
## TypeScript
The SDK is written in TypeScript and ships with complete type definitions. All request and response types are exported:
```typescript theme={null}
import type {
EnrichRequest,
EnrichSuccessResponse,
EnrichedTransaction,
MerchantData,
LocationData,
CategoryInfo,
EntityResult,
TransactionType,
EnrichmentFieldPath,
} from "triqai";
```
# Configuration
Source: https://docs.triqai.com/sdk/configuration
Configure the Triqai SDK with custom options for retries, timeouts, headers, and debug hooks
The `Triqai` client accepts a configuration object as the second argument. All options are optional and have sensible defaults.
## Full Configuration
```typescript theme={null}
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`),
});
```
## Options Reference
| Option | Type | Default | Description |
| ---------------- | ------------------------ | ------------------------ | ------------------------------------------------------------- |
| `baseUrl` | `string` | `https://api.triqai.com` | API base URL |
| `maxRetries` | `number` | `3` | Maximum number of retry attempts for transient errors |
| `retryDelay` | `number` | `500` | Base delay between retries in milliseconds |
| `maxRetryDelay` | `number` | `30000` | Maximum delay between retries in milliseconds |
| `timeout` | `number` | `60000` | Request timeout in milliseconds |
| `defaultHeaders` | `Record` | `{}` | Extra headers sent with every request |
| `onRequest` | `(info) => void` | — | Called before each request with method, URL, and headers |
| `onResponse` | `(info) => void` | — | Called after each response with status, duration, and headers |
## Debug Hooks
Use `onRequest` and `onResponse` to log or monitor API calls without modifying your application logic:
```typescript theme={null}
const triqai = new Triqai("triq_your_api_key", {
onRequest: (info) => {
console.log(`→ ${info.method} ${info.url}`);
},
onResponse: (info) => {
console.log(`← ${info.status} in ${info.durationMs}ms`);
// info.headers contains X-RateLimit-* values
},
});
```
## Environment Variables
A common pattern is to read the API key from an environment variable:
```typescript theme={null}
import Triqai from "triqai";
const triqai = new Triqai(process.env.TRIQAI_API_KEY!);
```
```bash .env theme={null}
TRIQAI_API_KEY=triq_your_api_key_here
```
# Error Handling
Source: https://docs.triqai.com/sdk/error-handling
Handle API errors with typed error classes in the Triqai SDK
All API errors are thrown as `TriqaiError` instances. Common HTTP statuses are mapped to typed subclasses so you can handle specific failure modes precisely.
## Basic Usage
```typescript theme={null}
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
Every error extends `TriqaiError` and includes `statusCode`, `message`, `code`, and `requestId` properties.
| Class | Status | When |
| -------------------------- | ------ | ------------------------------------------------ |
| `AuthenticationError` | 401 | Invalid or missing API key |
| `InsufficientCreditsError` | 402 | No credits remaining |
| `AuthorizationError` | 403 | Key valid but not authorized for this resource |
| `NotFoundError` | 404 | Resource does not exist |
| `DuplicateRequestError` | 409 | Idempotency key reused with different parameters |
| `ValidationError` | 422 | Request body validation failed |
| `RateLimitError` | 429 | Rate limit exceeded |
| `ClientDisconnectedError` | 499 | Client disconnected before response completed |
| `InternalServerError` | 500 | Server error (retried automatically) |
| `ServiceUnavailableError` | 503 | Service temporarily down (retried automatically) |
| `GatewayTimeoutError` | 504 | Upstream timeout |
| `ConnectionError` | — | Network or DNS failure |
| `TimeoutError` | — | Request exceeded configured timeout |
## Validation Errors
`ValidationError` includes a `fieldErrors` property with per-field error messages:
```typescript theme={null}
try {
await triqai.transactions.enrich({
title: "",
country: "INVALID",
type: "expense",
});
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.fieldErrors);
// { title: ["Title cannot be empty"], country: ["Invalid country code"] }
}
}
```
## Rate Limit Errors
`RateLimitError` includes a `rateLimitInfo` property with retry timing:
```typescript theme={null}
try {
await triqai.transactions.enrich({ ... });
} catch (err) {
if (err instanceof RateLimitError) {
console.log(err.rateLimitInfo.retryAfter); // seconds until reset
console.log(err.rateLimitInfo.limit); // max tokens
console.log(err.rateLimitInfo.remaining); // tokens left
}
}
```
# Node.js / TypeScript SDK
Source: https://docs.triqai.com/sdk/overview
Official Node.js / TypeScript SDK for the Triqai Transaction Enrichment API
The official [Node.js / TypeScript SDK](https://www.npmjs.com/package/triqai) for the Triqai Transaction Enrichment API.
Enrich raw bank transaction descriptions into structured data: merchants, categories, locations, intermediaries, and more — with full TypeScript support.
The SDK requires **Node.js 18+** (uses native `fetch`). It also works in **Bun**, **Deno**, and **Cloudflare Workers**.
## Installation
```bash npm theme={null}
npm install triqai
```
```bash yarn theme={null}
yarn add triqai
```
```bash pnpm theme={null}
pnpm add triqai
```
## Quick Start
```typescript theme={null}
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", ... } }, ...]
```
## What's Included
The SDK provides typed methods for every Triqai API endpoint:
Enrich, list, get, and delete transactions with full pagination support
Look up categories, merchants, locations, intermediaries, and issue reports
Typed error classes for every HTTP status with automatic retries
Timeouts, retries, debug hooks, custom headers, and more
## Next Steps
Customize timeouts, retries, headers, and debug hooks
See the full REST API documentation
# Resources
Source: https://docs.triqai.com/sdk/resources
Access categories, merchants, locations, intermediaries, and issue reports through the Triqai SDK
Beyond transactions, the SDK provides typed methods for looking up enrichment-related resources and submitting issue reports.
## Categories
List all available transaction categories:
```typescript theme={null}
const { categories, total, categoryVersion } =
await triqai.categories.list();
for (const cat of categories) {
console.log(`${cat.name} (level ${cat.level}, type: ${cat.type})`);
}
```
## Merchants
Look up a merchant by ID (returned in enrichment results):
```typescript theme={null}
const merchant = await triqai.merchants.get("merchant-uuid");
console.log(merchant.name, merchant.website, merchant.icon);
```
## Locations
Look up a location by ID:
```typescript theme={null}
const location = await triqai.locations.get("location-uuid");
console.log(location.formatted, location.structured.city);
```
## Intermediaries
Look up an intermediary (payment processor, wallet, P2P service) by ID:
```typescript theme={null}
const intermediary = await triqai.intermediaries.get("intermediary-uuid");
console.log(intermediary.name); // "PayPal", "Square", etc.
```
## Issue Reports
Report enrichment issues and track their resolution.
### Create a Report
```typescript theme={null}
const report = await triqai.issueReports.create({
transactionId: "tx-uuid",
description: "Merchant was identified incorrectly",
fields: ["entities.merchant.data.name"],
});
```
### List Reports
```typescript theme={null}
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 a Report
```typescript theme={null}
const report = await triqai.issueReports.get("report-uuid");
```
# Transactions
Source: https://docs.triqai.com/sdk/transactions
Enrich, list, retrieve, and delete transactions using the Triqai SDK
The `transactions` resource provides methods for enriching raw transaction strings and managing enriched transaction records.
## Enrich a Transaction
Transform a raw bank transaction string into structured data:
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "AMAZON MKTPLACE PMTS AMZN.COM/BILL WA",
country: "US",
type: "expense",
});
console.log(result.data.transaction.category.primary.name);
console.log(result.data.entities);
```
### Enrichment Options
Use the `options` field to control extraction behavior. You can skip entity types you don't need or pre-fill known data to improve accuracy:
```typescript theme={null}
const result = await triqai.transactions.enrich({
title: "AMAZON MKTPLACE PMTS AMZN.COM/BILL WA",
country: "US",
type: "expense",
options: {
filters: {
noLocation: true,
noIntermediary: true,
},
merchant: {
name: "Amazon",
},
},
});
```
You cannot combine a filter (e.g. `noMerchant`) with pre-filled data for the same entity type — the request will return a 422 validation error.
## Idempotent Enrichment
Pass an `idempotencyKey` to safely retry enrichment requests without consuming extra credits:
```typescript theme={null}
const result = await triqai.transactions.enrich(
{ title: "STARBUCKS", country: "US", type: "expense" },
{ idempotencyKey: "my-unique-key-123" },
);
```
## List Transactions
Retrieve paginated lists of previously enriched transactions:
```typescript theme={null}
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 }
if (page.hasNextPage) {
const nextPage = await page.nextPage();
}
```
### Auto-Paginate
Use `for await...of` to iterate through all pages automatically:
```typescript theme={null}
const page = await triqai.transactions.list();
for await (const tx of page) {
console.log(tx.id, tx.raw, tx.status);
}
```
## Get a Transaction
Retrieve a single enriched transaction by ID:
```typescript theme={null}
const tx = await triqai.transactions.get(
"550e8400-e29b-41d4-a716-446655440000"
);
```
## Delete a Transaction
Remove a transaction record:
```typescript theme={null}
const { deleted, transactionId } = await triqai.transactions.delete(
"550e8400-e29b-41d4-a716-446655440000"
);
```