Delete Tax

The Delete Tax endpoint permanently removes a tax configuration from your business. This operation requires careful consideration as it may affect existing invoices, payment links, and other entities that reference the tax. The endpoint includes strict access controls to ensure only authorized applications can delete taxes.

Endpoint DetailsCopied!

  • URL: /api/v0/taxes/{id}

  • Method: DELETE

  • Authentication: Required (API Key Authentication)

  • Content-Type: application/json

AuthenticationCopied!

This endpoint requires API key authentication using:

  • x-client-key: Your API client key

  • x-client-secret: Your API client secret

Path ParametersCopied!

Required Parameters

Parameter

Type

Description

Validation

id

string

Unique identifier of the tax to delete

Must be a valid UUID v4

Request ExamplesCopied!

Basic Request

curl -X DELETE https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret"

JavaScript/Fetch

const taxId = "123e4567-e89b-12d3-a456-426614174000";

const response = await fetch(`/api/v0/taxes/${taxId}`, {
  method: 'DELETE',
  headers: {
    'x-client-key': 'your-client-key',
    'x-client-secret': 'your-client-secret'
  }
});

const deletedTax = await response.json();

cURL with Error Handling

curl -X DELETE https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret" \
  -w "HTTP Status: %{http_code}\n" \
  -o response.json

ResponseCopied!

Success Response (200 OK)

Returns the deleted tax object for confirmation:

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "VAT",
  "description": "Value Added Tax",
  "business_id": "456e7890-e89b-12d3-a456-426614174001",
  "percentage": 20.0,
  "active": true,
  "created_at": "2024-01-15T10:30:00.000Z",
  "updated_at": "2024-01-20T14:45:00.000Z",
  "apps": [
    {
      "id": "789e0123-e89b-12d3-a456-426614174002",
      "business_id": "456e7890-e89b-12d3-a456-426614174001",
      "app_name": "eu-store",
      "display_name": "European Store",
      "environment": "PRODUCTION",
      "stage": "PRODUCTION",
      "timezone": "Europe/London",
      "created_at": "2024-01-10T08:00:00.000Z",
      "updated_at": "2024-01-10T08:00:00.000Z"
    }
  ]
}

Error Responses

401 Unauthorized - Missing Authentication
{
  "statusCode": 401,
  "message": "Application not authenticated",
  "error": "Unauthorized"
}
401 Unauthorized - Access Denied
{
  "statusCode": 401,
  "message": "Access denied to this tax",
  "error": "Unauthorized"
}
404 Not Found
{
  "statusCode": 404,
  "message": "Tax not found",
  "error": "Not Found"
}
400 Bad Request - Invalid UUID
{
  "statusCode": 400,
  "message": "Validation failed (uuid v4 is expected)",
  "error": "Bad Request"
}
409 Conflict - Tax In Use
{
  "statusCode": 409,
  "message": "Cannot delete tax: it is referenced by existing invoices or payment links",
  "error": "Conflict"
}

Business LogicCopied!

Access Control

  • Only applications that are currently associated with the tax can delete it

  • The system verifies that your authenticated application has access before allowing deletion

  • Cross-application access is strictly prohibited for security

Referential Integrity

  • The system may prevent deletion if the tax is referenced by:

    • Active invoices

    • Payment links

    • Other entities that depend on the tax configuration

  • This prevents data integrity issues and maintains audit trails

Permanent Deletion

  • This operation permanently removes the tax from the database

  • The tax cannot be recovered after deletion

  • Consider deactivating instead of deleting if historical records are important

Use CasesCopied!

1. Remove Unused Tax Configurations

Delete taxes that are no longer needed:

async function deleteUnusedTax(taxId) {
  try {
    const response = await fetch(`/api/v0/taxes/${taxId}`, {
      method: 'DELETE',
      headers: {
        'x-client-key': 'your-client-key',
        'x-client-secret': 'your-client-secret'
      }
    });

    if (!response.ok) {
      throw new Error(`Failed to delete tax: ${response.statusText}`);
    }

    const deletedTax = await response.json();
    console.log(`Successfully deleted tax: ${deletedTax.name}`);
    
    return deletedTax;
  } catch (error) {
    console.error('Error deleting tax:', error);
    throw error;
  }
}

2. Cleanup Test Data

Remove test tax configurations:

async function cleanupTestTaxes(testTaxIds) {
  const deletionResults = [];
  
  for (const taxId of testTaxIds) {
    try {
      const deletedTax = await deleteUnusedTax(taxId);
      deletionResults.push({ success: true, tax: deletedTax });
    } catch (error) {
      deletionResults.push({ 
        success: false, 
        taxId, 
        error: error.message 
      });
    }
  }
  
  return deletionResults;
}

3. Safe Deletion with Validation

Check for references before deletion:

async function safeDeleteTax(taxId) {
  // First, check if tax is in use
  const usage = await checkTaxUsage(taxId);
  
  if (usage.invoiceCount > 0) {
    throw new Error(
      `Cannot delete tax: it is used in ${usage.invoiceCount} invoices`
    );
  }
  
  if (usage.paymentLinkCount > 0) {
    throw new Error(
      `Cannot delete tax: it is used in ${usage.paymentLinkCount} payment links`
    );
  }
  
  // Safe to delete
  return await deleteUnusedTax(taxId);
}

async function checkTaxUsage(taxId) {
  // Implementation would check for references
  // This is pseudocode - actual implementation would query your data
  return {
    invoiceCount: 0,
    paymentLinkCount: 0
  };
}

4. Batch Tax Cleanup

Delete multiple taxes with error handling:

async function batchDeleteTaxes(taxIds) {
  const results = {
    successful: [],
    failed: [],
    total: taxIds.length
  };
  
  for (const taxId of taxIds) {
    try {
      const deletedTax = await deleteUnusedTax(taxId);
      results.successful.push({
        id: taxId,
        name: deletedTax.name
      });
    } catch (error) {
      results.failed.push({
        id: taxId,
        error: error.message
      });
    }
  }
  
  console.log(`Deletion complete: ${results.successful.length} successful, ${results.failed.length} failed`);
  return results;
}

Integration ExamplesCopied!

React Component with Confirmation

import React, { useState } from 'react';

function TaxDeleteButton({ tax, onDeleted, onError }) {
  const [isDeleting, setIsDeleting] = useState(false);
  const [showConfirmation, setShowConfirmation] = useState(false);

  const handleDelete = async () => {
    setIsDeleting(true);
    
    try {
      const response = await fetch(`/api/v0/taxes/${tax.id}`, {
        method: 'DELETE',
        headers: {
          'x-client-key': process.env.REACT_APP_CLIENT_KEY,
          'x-client-secret': process.env.REACT_APP_CLIENT_SECRET
        }
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || 'Failed to delete tax');
      }

      const deletedTax = await response.json();
      onDeleted(deletedTax);
      setShowConfirmation(false);
    } catch (error) {
      onError(error.message);
    } finally {
      setIsDeleting(false);
    }
  };

  if (showConfirmation) {
    return (
      <div className="tax-delete-confirmation">
        <p>Are you sure you want to delete "{tax.name}" ({tax.percentage}%)?</p>
        <p><strong>This action cannot be undone.</strong></p>
        <div>
          <button 
            onClick={handleDelete} 
            disabled={isDeleting}
            className="btn-danger"
          >
            {isDeleting ? 'Deleting...' : 'Yes, Delete'}
          </button>
          <button 
            onClick={() => setShowConfirmation(false)}
            disabled={isDeleting}
            className="btn-secondary"
          >
            Cancel
          </button>
        </div>
      </div>
    );
  }

  return (
    <button 
      onClick={() => setShowConfirmation(true)}
      className="btn-danger"
      title="Delete tax"
    >
      Delete
    </button>
  );
}

Python Service

import requests
from typing import Dict, Any, List

class TaxDeletionService:
    def __init__(self, client_key: str, client_secret: str, base_url: str):
        self.client_key = client_key
        self.client_secret = client_secret
        self.base_url = base_url
        
    def _get_headers(self) -> Dict[str, str]:
        return {
            'x-client-key': self.client_key,
            'x-client-secret': self.client_secret
        }
    
    def delete_tax(self, tax_id: str) -> Dict[str, Any]:
        """Delete a tax configuration"""
        response = requests.delete(
            f"{self.base_url}/api/v0/taxes/{tax_id}",
            headers=self._get_headers()
        )
        
        if response.status_code == 200:
            return response.json()
        elif response.status_code == 404:
            raise ValueError(f"Tax with ID {tax_id} not found")
        elif response.status_code == 401:
            raise PermissionError("Access denied to this tax")
        elif response.status_code == 409:
            raise ValueError("Tax is in use and cannot be deleted")
        else:
            response.raise_for_status()
    
    def safe_delete_tax(self, tax_id: str) -> Dict[str, Any]:
        """Delete a tax with additional safety checks"""
        try:
            # Get tax details first
            tax_response = requests.get(
                f"{self.base_url}/api/v0/taxes/{tax_id}",
                headers=self._get_headers()
            )
            
            if tax_response.status_code != 200:
                raise ValueError("Tax not found or access denied")
                
            tax = tax_response.json()
            
            # Log the deletion attempt
            print(f"Attempting to delete tax: {tax['name']} ({tax['percentage']}%)")
            
            # Perform deletion
            deleted_tax = self.delete_tax(tax_id)
            
            print(f"Successfully deleted tax: {deleted_tax['name']}")
            return deleted_tax
            
        except Exception as e:
            print(f"Failed to delete tax {tax_id}: {str(e)}")
            raise
    
    def batch_delete(self, tax_ids: List[str]) -> Dict[str, List]:
        """Delete multiple taxes with error handling"""
        results = {
            'successful': [],
            'failed': []
        }
        
        for tax_id in tax_ids:
            try:
                deleted_tax = self.delete_tax(tax_id)
                results['successful'].append({
                    'id': tax_id,
                    'name': deleted_tax['name']
                })
            except Exception as e:
                results['failed'].append({
                    'id': tax_id,
                    'error': str(e)
                })
        
        return results

# Usage
tax_service = TaxDeletionService('your-key', 'your-secret', 'https://api.devdraft.com')

# Delete single tax
try:
    deleted_tax = tax_service.safe_delete_tax('123e4567-e89b-12d3-a456-426614174000')
    print(f"Deleted: {deleted_tax['name']}")
except Exception as e:
    print(f"Deletion failed: {e}")

# Batch delete
tax_ids = ['tax-1', 'tax-2', 'tax-3']
results = tax_service.batch_delete(tax_ids)
print(f"Successful: {len(results['successful'])}, Failed: {len(results['failed'])}")

Node.js/Express with Transaction Support

const express = require('express');
const axios = require('axios');

class TaxDeletionManager {
  constructor(clientKey, clientSecret, baseUrl) {
    this.clientKey = clientKey;
    this.clientSecret = clientSecret;
    this.baseUrl = baseUrl;
  }

  async deleteTax(taxId) {
    try {
      const response = await axios.delete(
        `${this.baseUrl}/api/v0/taxes/${taxId}`,
        {
          headers: {
            'x-client-key': this.clientKey,
            'x-client-secret': this.clientSecret
          }
        }
      );

      return response.data;
    } catch (error) {
      if (error.response) {
        const { status, data } = error.response;
        
        switch (status) {
          case 404:
            throw new Error('Tax not found');
          case 401:
            throw new Error('Access denied to this tax');
          case 409:
            throw new Error('Tax is in use and cannot be deleted');
          default:
            throw new Error(data.message || 'Failed to delete tax');
        }
      }
      throw error;
    }
  }

  async safeDeleteWithBackup(taxId) {
    // Get tax details for backup before deletion
    const tax = await this.getTax(taxId);
    
    try {
      const deletedTax = await this.deleteTax(taxId);
      
      // Log successful deletion
      console.log(`Tax deleted: ${deletedTax.name} (${deletedTax.percentage}%)`);
      
      return {
        success: true,
        deletedTax,
        backup: tax
      };
    } catch (error) {
      console.error(`Failed to delete tax ${taxId}:`, error.message);
      
      return {
        success: false,
        error: error.message,
        backup: tax
      };
    }
  }

  async getTax(taxId) {
    const response = await axios.get(
      `${this.baseUrl}/api/v0/taxes/${taxId}`,
      {
        headers: {
          'x-client-key': this.clientKey,
          'x-client-secret': this.clientSecret
        }
      }
    );
    
    return response.data;
  }
}

// Express routes
const app = express();
const deletionManager = new TaxDeletionManager(
  process.env.CLIENT_KEY,
  process.env.CLIENT_SECRET,
  'https://api.devdraft.com'
);

app.delete('/taxes/:id', async (req, res) => {
  try {
    const result = await deletionManager.safeDeleteWithBackup(req.params.id);
    
    if (result.success) {
      res.json({
        message: 'Tax deleted successfully',
        deletedTax: result.deletedTax
      });
    } else {
      res.status(400).json({
        message: 'Failed to delete tax',
        error: result.error,
        backup: result.backup
      });
    }
  } catch (error) {
    console.error('Tax deletion error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Batch deletion endpoint
app.delete('/taxes/batch', async (req, res) => {
  const { taxIds } = req.body;
  
  if (!Array.isArray(taxIds)) {
    return res.status(400).json({ error: 'taxIds must be an array' });
  }
  
  const results = [];
  
  for (const taxId of taxIds) {
    const result = await deletionManager.safeDeleteWithBackup(taxId);
    results.push({ taxId, ...result });
  }
  
  const successful = results.filter(r => r.success).length;
  const failed = results.length - successful;
  
  res.json({
    summary: { total: results.length, successful, failed },
    results
  });
});

Safety ConsiderationsCopied!

1. Pre-Deletion Validation

Always check for references before deletion:

async function validateTaxDeletion(taxId) {
  const checks = [
    { name: 'Active Invoices', check: () => checkActiveInvoices(taxId) },
    { name: 'Payment Links', check: () => checkPaymentLinks(taxId) },
    { name: 'Future Scheduled Items', check: () => checkScheduledItems(taxId) }
  ];
  
  const issues = [];
  
  for (const { name, check } of checks) {
    const count = await check();
    if (count > 0) {
      issues.push(`${name}: ${count} items`);
    }
  }
  
  if (issues.length > 0) {
    throw new Error(`Cannot delete tax. In use by: ${issues.join(', ')}`);
  }
  
  return true;
}

2. Backup Before Deletion

Create backups for important taxes:

async function deleteWithBackup(taxId) {
  // Create backup
  const tax = await fetchTax(taxId);
  const backup = {
    ...tax,
    deletedAt: new Date().toISOString(),
    deletedBy: getCurrentUserId()
  };
  
  // Store backup
  await storeBackup(backup);
  
  // Perform deletion
  const deletedTax = await deleteTax(taxId);
  
  return { deletedTax, backup };
}

3. Confirmation Mechanisms

Implement confirmation for critical deletions:

async function confirmAndDeleteTax(taxId, confirmationCode) {
  const expectedCode = await generateConfirmationCode(taxId);
  
  if (confirmationCode !== expectedCode) {
    throw new Error('Invalid confirmation code');
  }
  
  return await deleteTax(taxId);
}

Rate LimitingCopied!

This endpoint is subject to the standard API rate limits:

  • Production: 1000 requests per hour per API key

  • Development: 100 requests per hour per API key

Best PracticesCopied!

1. Use Deactivation Instead of Deletion

For taxes that might be needed for historical records:

// Instead of deleting, consider deactivating
async function deactivateTax(taxId) {
  return await updateTax(taxId, { active: false });
}

2. Implement Soft Deletion

For applications requiring audit trails:

// Mark as deleted but keep in database
async function softDeleteTax(taxId) {
  return await updateTax(taxId, { 
    active: false,
    deleted: true,
    deletedAt: new Date().toISOString()
  });
}

3. Log All Deletions

Maintain audit logs for compliance:

async function deleteWithAudit(taxId, userId, reason) {
  const tax = await fetchTax(taxId);
  
  // Log deletion attempt
  await logAuditEvent({
    action: 'TAX_DELETE',
    userId,
    entityId: taxId,
    entityData: tax,
    reason,
    timestamp: new Date().toISOString()
  });
  
  const deletedTax = await deleteTax(taxId);
  
  // Log successful deletion
  await logAuditEvent({
    action: 'TAX_DELETED',
    userId,
    entityId: taxId,
    timestamp: new Date().toISOString()
  });
  
  return deletedTax;
}

4. Error Recovery

Implement error recovery mechanisms:

async function deleteWithRecovery(taxId) {
  try {
    return await deleteTax(taxId);
  } catch (error) {
    if (error.message.includes('in use')) {
      // Suggest alternatives
      throw new Error(
        'Tax is in use. Consider deactivating instead of deleting, ' +
        'or remove all references first.'
      );
    }
    throw error;
  }
}

Security and ComplianceCopied!

Access Control

  • Application Scoping: Only associated applications can delete taxes

  • Business Isolation: Taxes from other businesses are completely inaccessible

  • Authentication Required: All requests must include valid API credentials

Data Protection

  • Permanent Deletion: Data is permanently removed and cannot be recovered

  • Audit Logging: Consider implementing deletion logs for compliance

  • Backup Strategy: Implement backup mechanisms for critical tax configurations

Compliance Considerations

  • Financial Records: Consider legal requirements for maintaining tax records

  • Historical Data: Deletion may affect historical reporting and audit trails

  • Regulatory Requirements: Some jurisdictions may require tax configuration retention