Skip to main content
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:
{
  "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:
{
  "success": true,
  "partial": false,
  "data": {
    "transaction": {
      "category": { "primary": { "name": "Coffee Shops" }, "confidence": { "value": 95, "reasons": ["merchant_category_match"] } },
      "subscription": { "recurring": false, "type": null },
      "channel": "in_store",
      "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:
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:
{
  "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:
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:
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:
{
  "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:
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

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

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

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

Subscription Detection

const { subscription } = result.data.transaction;

if (subscription.recurring) {
  console.log("Subscription type:", subscription.type);
  // "streaming", "software", "news", "fitness", "mobile", "gaming", "utilities", etc.
}

Complete Processing Example

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,
      channel: transaction.channel,
      confidence: transaction.confidence.value,
      subscription: transaction.subscription.recurring ? transaction.subscription.type : null,

      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

Error Handling

Handle errors and edge cases

Confidence Scores

Interpret confidence values and reason tags