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:
{
  "success": true,
  "partial": false,
  "data": {
    "transaction": { /* category, subscription, channel, confidence */ },
    "enrichments": {
      "merchant": { "status": "found", /* ... */ },
      "location": { "status": "found", /* ... */ },
      "paymentProcessor": { "status": "not_applicable", /* ... */ },
      "peerToPeer": { "status": "not_applicable", /* ... */ }
    }
  }
}
Processing:
if (response.success && !response.partial) {
  // All enrichments succeeded - process normally
  const merchant = response.data.enrichments.merchant.data;
  const location = response.data.enrichments.location.data;
  // ...
}

Partial Success

When some enrichment modules succeed and others fail:
{
  "success": true,
  "partial": true,
  "data": {
    "transaction": { /* ... */ },
    "enrichments": {
      "merchant": { "status": "found", "confidence": 95, "data": { /* ... */ } },
      "location": { "status": "no_match", "confidence": null, "data": null }
    }
  },
  "meta": {
    "errors": ["location_timeout"]
  }
}
Processing:
if (response.success && response.partial) {
  // Some enrichments succeeded - use what's available
  if (response.data.enrichments.merchant.status === 'found') {
    const merchant = response.data.enrichments.merchant.data;
    // Process merchant data
  }
  
  // Check which enrichers failed
  if (response.meta.errors) {
    console.log('Failed enrichers:', response.meta.errors);
  }
}

Enrichment Statuses

Each enrichment module returns a status:
StatusMeaningdataconfidence
foundEntity identifiedObjectNumber
no_matchCould not identifynullnull
not_applicableModule doesn’t applynullnull

Handling Each Status

function processEnrichment(enrichment, type) {
  switch (enrichment.status) {
    case 'found':
      return {
        available: true,
        data: enrichment.data,
        confidence: enrichment.confidence
      };
    
    case 'no_match':
      return {
        available: false,
        reason: `No ${type} could be identified`
      };
    
    case 'not_applicable':
      return {
        available: false,
        reason: `${type} not relevant for this transaction`
      };
    
    default:
      return {
        available: false,
        reason: 'Unknown status'
      };
  }
}

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

async function enrichTransaction(tx) {
  const response = await fetch('https://api.triqai.com/v1/transactions/enrich', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.TRIQAI_API_KEY
    },
    body: JSON.stringify(tx)
  });

  const result = await response.json();

  if (!result.success) {
    throw new EnrichmentError(result.error);
  }

  return result;
}

class EnrichmentError extends Error {
  constructor(error) {
    super(error.message);
    this.code = error.code;
    this.details = error.details;
  }
}

Working with Enrichment Data

Merchant Data

function displayMerchant(merchant) {
  if (merchant.status !== 'found') {
    return null;
  }

  return {
    name: merchant.data.name,
    logo: merchant.data.icon,
    website: merchant.data.website,
    // Use brand color for UI theming
    brandColor: merchant.data.color
  };
}

Location Data

function displayLocation(location) {
  if (location.status !== 'found') {
    return null;
  }

  const { structured } = location.data;
  
  return {
    formatted: location.data.formatted,
    city: structured.city,
    country: structured.countryName,
    coordinates: structured.coordinates,
    timezone: structured.timezone
  };
}

Category Data

function displayCategory(category) {
  const parts = [
    category.primary.name,
    category.secondary?.name,
    category.tertiary?.name
  ].filter(Boolean);

  return {
    full: parts.join(' > '),
    primary: category.primary.name,
    confidence: category.confidence,
    mcc: category.primary.code.mcc
  };
}

Subscription Detection

function checkSubscription(transaction) {
  const { subscription } = transaction;
  
  if (!subscription.recurring) {
    return { isSubscription: false };
  }

  return {
    isSubscription: true,
    type: subscription.type, // 'streaming', 'software', etc.
    label: getSubscriptionLabel(subscription.type)
  };
}

function getSubscriptionLabel(type) {
  const labels = {
    streaming: 'Streaming Service',
    software: 'Software Subscription',
    news: 'News & Media',
    fitness: 'Fitness',
    mobile: 'Mobile Plan',
    gaming: 'Gaming',
    utilities: 'Utilities',
    other: 'Subscription'
  };
  
  return labels[type] || 'Subscription';
}

Complete Processing Example

function processEnrichmentResponse(response) {
  // Handle errors
  if (!response.success) {
    return {
      success: false,
      error: response.error.message,
      code: response.error.code
    };
  }

  const { data, meta, partial } = response;
  const { transaction, enrichments } = data;

  // Build result object
  const result = {
    success: true,
    partial,
    requestId: meta.requestId,
    
    // Core transaction data
    category: {
      primary: transaction.category.primary.name,
      secondary: transaction.category.secondary?.name,
      confidence: transaction.category.confidence
    },
    channel: transaction.channel,
    confidence: transaction.confidence,
    
    // Subscription
    subscription: transaction.subscription.recurring 
      ? transaction.subscription.type 
      : null,
    
    // Merchant
    merchant: enrichments.merchant.status === 'found' 
      ? {
          name: enrichments.merchant.data.name,
          logo: enrichments.merchant.data.icon,
          website: enrichments.merchant.data.website
        }
      : null,
    
    // Location
    location: enrichments.location.status === 'found'
      ? {
          formatted: enrichments.location.data.formatted,
          coordinates: enrichments.location.data.structured.coordinates
        }
      : null,
    
    // Payment processor
    paymentProcessor: enrichments.paymentProcessor.status === 'found'
      ? enrichments.paymentProcessor.data.name
      : null,
    
    // P2P
    p2p: enrichments.peerToPeer.status === 'found'
      ? {
          platform: enrichments.peerToPeer.data.platform?.name,
          recipient: enrichments.peerToPeer.data.recipient?.displayName
        }
      : null
  };

  // Log warnings for partial results
  if (partial && meta.errors) {
    console.warn('Partial enrichment:', meta.errors);
  }

  return result;
}

Best Practices

Before accessing data, verify success === true.
Don’t fail if some enrichments are missing. Use what’s available.
Always check status === 'found' before accessing .data.
Show confidence indicators to users or flag low-confidence results.
Keep the meta.requestId for debugging and issue reports.

Next Steps