Skip to main content
Transaction processing is the core operational phase of your STaaS platform. This phase combines L1 (transaction sync for compliance) and L2 (real-time tax calculation during checkout). You sync completed transactions to Kintsugi and, when the tax engine is enabled, calculate tax at checkout. All endpoints can be accessed via HTTP REST or the optional Kintsugi SDK.

Transaction Processing Flow

Step 1: Calculate Tax Estimate

Endpoint: POST /v1/tax/estimate
Access Method: HTTP REST
Purpose: Calculates estimated tax for a transaction before payment is processed.
When to Use:
  • During checkout process (before payment)
  • Quote generation
  • Shopping cart tax calculation
  • Real-time tax display for customers
Request Example:
{
  "transaction_date": "2024-01-15",
  "customer": {
    "address": {
      "street1": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94105",
      "country": "US"
    }
  },
  "line_items": [
    {
      "product_id": "prod_123",
      "quantity": 2,
      "unit_price": 50.00
    }
  ],
  "shipping": {
    "amount": 10.00
  }
}
Headers Required:
  • x-api-key: Organization’s API key
  • x-organization-id: Organization ID
  • Content-Type: application/json
Integration Notes:
  • Returns tax breakdown by jurisdiction
  • Includes total tax amount and tax rates
  • Used for display to customers before purchase
  • Does not create a permanent transaction record
  • Consider caching results for identical requests
  • Address validation may occur during calculation
Response Structure:
{
  "total_tax": 8.75,
  "tax_breakdown": [
    {
      "jurisdiction": "CA",
      "rate": 0.0875,
      "amount": 8.75
    }
  ],
  "line_items": [
    {
      "product_id": "prod_123",
      "tax": 8.75
    }
  ]
}
Example Implementation:
async function calculateTax(checkoutData, organizationId, apiKey) {
  const response = await fetch('https://api.trykintsugi.com/v1/tax/estimate', {
    method: 'POST',
    headers: {
      'x-api-key': apiKey,
      'x-organization-id': organizationId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      transaction_date: checkoutData.date,
      customer: {
        address: checkoutData.shippingAddress
      },
      line_items: checkoutData.items.map(item => ({
        product_id: item.productId,
        quantity: item.quantity,
        unit_price: item.price
      })),
      shipping: {
        amount: checkoutData.shippingAmount
      }
    })
  });
  
  return await response.json();
}

Step 2: Create Transaction

Endpoint: POST /v1/transactions
Access Method: HTTP REST
Purpose: Records a completed transaction for tax compliance and filing purposes.
When to Use:
  • After payment is confirmed
  • Recording sales for tax compliance
  • Creating audit trail for filings
  • Must be called after tax estimate (if estimate was used)
Request Example:
{
  "transaction_id": "TXN-001",
  "transaction_date": "2024-01-15",
  "customer": {
    "name": "John Doe",
    "email": "john@example.com",
    "address": {
      "street1": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94105",
      "country": "US"
    }
  },
  "line_items": [
    {
      "product_id": "prod_123",
      "quantity": 2,
      "unit_price": 50.00,
      "tax": 8.75
    }
  ],
  "subtotal": 100.00,
  "tax": 8.75,
  "total": 118.75,
  "shipping": {
    "amount": 10.00,
    "taxable": false
  }
}
Headers Required:
  • x-api-key: Organization’s API key
  • x-organization-id: Organization ID
  • Content-Type: application/json
Integration Notes:
  • Creates permanent transaction record
  • Transaction ID should be unique (use your order ID)
  • Tax amounts should match calculated estimates
  • Transactions are automatically included in filings
  • Address validation occurs during creation
  • Returns transaction with Kintsugi transaction ID
Transaction States: Transactions can have statuses: PENDING, COMMITTED, CANCELLED, REFUNDED, etc. Example Implementation:
async function createTransaction(orderData, taxEstimate, organizationId, apiKey) {
  const response = await fetch('https://api.trykintsugi.com/v1/transactions', {
    method: 'POST',
    headers: {
      'x-api-key': apiKey,
      'x-organization-id': organizationId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      external_id: orderData.orderId,
      date: orderData.date,
      type: "SALE",
      status: "COMMITTED",
      currency: "USD",
      total_amount: orderData.total,
      customer: {
        name: orderData.customerName,
        email: orderData.customerEmail,
        address: orderData.shippingAddress
      },
      addresses: [
        {
          type: "SHIP_TO",
          ...orderData.shippingAddress
        }
      ],
      transaction_items: orderData.items.map((item, index) => ({
        external_product_id: item.productId,
        quantity: item.quantity,
        amount: item.price * item.quantity,
        tax_amount_calculated: taxEstimate.line_items[index].tax
      }))
    })
  });
  
  const transaction = await response.json();
  
  // Store Kintsugi transaction ID
  await db.orders.update(orderData.orderId, {
    kintsugiTransactionId: transaction.id
  });
  
  return transaction;
}

Step 3: Get Transactions

Endpoint: SDK Method - kintsugi.transactions.get()
Access Method: SDK
Purpose: Retrieves a paginated list of transactions for reporting and reconciliation.
When to Use:
  • Client transaction history views
  • Reconciliation reports
  • Audit trail access
  • Dashboard displays
SDK Usage:
const kintsugi = new SDK({
  apiKey: organizationApiKey,
  organizationId: organizationId,
  serverURL: 'https://api.trykintsugi.com'
});

const queryParams = {
  limit: 50,
  offset: 0,
  startDate: "2024-01-01",
  endDate: "2024-12-31",
  // Optional filters: state_code, country, status, etc.
};

const response = await kintsugi.transactions.get(queryParams, {
  'x-organization-id': organizationId
});
Key Parameters:
  • limit: Number of results (default: 50)
  • offset: Pagination offset
  • startDate: Filter start date (ISO 8601)
  • endDate: Filter end date (ISO 8601)
  • Optional filters: state_code, country, status, transaction_type
Integration Notes:
  • Returns paginated results
  • Use date ranges for period-specific reports
  • Filter by status for reconciliation workflows
  • Large result sets require pagination
  • Used for transaction history displays in client dashboards
Example: Dashboard Display:
async function getTransactionHistory(organizationId, startDate, endDate) {
  const transactions = await kintsugi.transactions.get({
    limit: 100,
    offset: 0,
    startDate: startDate,
    endDate: endDate,
    status: "COMMITTED"
  }, { 'x-organization-id': organizationId });
  
  return transactions.data.map(txn => ({
    id: txn.id,
    externalId: txn.external_id,
    date: txn.date,
    total: txn.total_amount,
    tax: txn.total_tax_amount_calculated,
    status: txn.status
  }));
}

Step 4: Get Transaction by ID

Endpoint: SDK Method - kintsugi.transactions.getById()
Access Method: SDK
Purpose: Retrieves detailed information about a specific transaction.
When to Use:
  • Transaction detail views
  • Dispute resolution
  • Audit inquiries
  • Individual transaction lookup
SDK Usage:
const response = await kintsugi.transactions.getById({
  transactionId: "txn_123456"
}, {
  'x-organization-id': organizationId
});
Key Parameters:
  • transactionId: Kintsugi transaction ID (returned from create transaction)
Integration Notes:
  • Returns full transaction details including line items, tax breakdown, customer info
  • Use for detailed transaction views
  • Transaction ID is Kintsugi’s internal ID, not your external transaction ID

Data Flow: Checkout to Compliance

Best Practices

  1. Match Tax Amounts: Use tax amounts from estimate when creating transaction
  2. Store Transaction IDs: Save Kintsugi transaction IDs for future reference
  3. Handle Errors: Implement retry logic for transient failures
  4. Validate Before Sending: Ensure all required fields are present
  5. Use Unique IDs: Use your order ID as external_id to prevent duplicates
  6. Status Management: Set appropriate transaction status (COMMITTED for completed sales)
  7. Address Validation: Validate addresses before sending to improve tax accuracy

Error Handling

Common Errors:
  • 400 Bad Request: Missing required fields or invalid data format
  • 404 Not Found: Product doesn’t exist - create product first
  • 409 Conflict: Duplicate transaction ID - use unique external_id
  • 401 Unauthorized: Invalid API key - verify credentials
Example Error Handling:
async function createTransactionWithRetry(transactionData, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await createTransaction(transactionData);
    } catch (error) {
      if (error.status === 409 && i < maxRetries - 1) {
        // Duplicate ID - append suffix
        transactionData.external_id = `${transactionData.external_id}-${i + 1}`;
        continue;
      }
      if (error.status === 404) {
        // Product missing - create it first
        await createProduct(transactionData.product);
        continue;
      }
      throw error;
    }
  }
}