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 |
---|---|---|---|
|
|
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