Update Tax

The Update Tax endpoint enables you to modify existing tax configurations. This endpoint supports partial updates, allowing you to change specific fields while preserving others. The system maintains strict access control to ensure only authorized applications can modify tax settings.

Endpoint DetailsCopied!

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

  • Method: PUT

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

Must be a valid UUID v4

Request BodyCopied!

Required Fields

Field

Type

Description

Validation

id

string

Tax ID (must match path parameter)

Required, valid UUID v4

Optional Fields

Field

Type

Description

Validation

name

string

Updated tax name

Optional string

description

string

Updated tax description

Optional string

percentage

number

Updated tax percentage rate

Optional number

active

boolean

Updated active status

Optional boolean

appIds

string[]

Updated list of associated application IDs

Optional array of valid UUIDs

Request Schema

{
  "id": "string",
  "name": "string",
  "description": "string",
  "percentage": "number",
  "active": "boolean",
  "appIds": ["string"]
}

ResponseCopied!

Success Response (200 OK)

{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "Updated VAT",
  "description": "Updated Value Added Tax for European Union transactions",
  "business_id": "456e7890-e89b-12d3-a456-426614174001",
  "percentage": 21.0,
  "active": true,
  "created_at": "2024-01-15T10:30:00.000Z",
  "updated_at": "2024-01-25T16:20: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"
    },
    {
      "id": "890e1234-e89b-12d3-a456-426614174003",
      "business_id": "456e7890-e89b-12d3-a456-426614174001",
      "app_name": "new-marketplace",
      "display_name": "New Marketplace",
      "environment": "PRODUCTION",
      "stage": "PRODUCTION",
      "timezone": "Europe/Berlin",
      "created_at": "2024-01-20T14:00:00.000Z",
      "updated_at": "2024-01-20T14:00:00.000Z"
    }
  ]
}

Error Responses

400 Bad Request - Validation Error
{
  "statusCode": 400,
  "message": [
    "id must be a UUID",
    "percentage must be a number"
  ],
  "error": "Bad Request"
}
400 Bad Request - ID Mismatch
{
  "statusCode": 400,
  "message": "Tax ID in path and body must match",
  "error": "Bad Request"
}
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"
}

Business LogicCopied!

Access Control

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

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

  • Cross-application access is strictly prohibited for security

Automatic App Association

  • Your current application ID is automatically included in the appIds array

  • Even if you don't include your app ID in the appIds field, it will be added

  • This ensures the updating application maintains access to the tax

Partial Updates

  • Only provided fields are updated; omitted fields retain their current values

  • The id field is required but used for validation (must match path parameter)

  • Empty arrays or null values will update the field accordingly

Example RequestsCopied!

Update Tax Rate Only

curl -X PUT https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "Content-Type: application/json" \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret" \
  -d '{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "percentage": 21.0
  }'

Update Name and Description

curl -X PUT https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "Content-Type: application/json" \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret" \
  -d '{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "name": "Updated VAT Rate",
    "description": "New VAT rate effective from 2024"
  }'

Deactivate Tax

curl -X PUT https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "Content-Type: application/json" \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret" \
  -d '{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "active": false
  }'

Update Associated Applications

curl -X PUT https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "Content-Type: application/json" \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret" \
  -d '{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "appIds": [
      "789e0123-e89b-12d3-a456-426614174002",
      "890e1234-e89b-12d3-a456-426614174003",
      "901e2345-e89b-12d3-a456-426614174004"
    ]
  }'

Complete Tax Update

curl -X PUT https://api.devdraft.com/api/v0/taxes/123e4567-e89b-12d3-a456-426614174000 \
  -H "Content-Type: application/json" \
  -H "x-client-key: your-client-key" \
  -H "x-client-secret: your-client-secret" \
  -d '{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "name": "EU VAT Standard Rate",
    "description": "Standard VAT rate for European Union member states",
    "percentage": 19.0,
    "active": true,
    "appIds": [
      "789e0123-e89b-12d3-a456-426614174002",
      "890e1234-e89b-12d3-a456-426614174003"
    ]
  }'

Use CasesCopied!

1. Tax Rate Adjustments

Update tax rates due to regulatory changes:

async function updateTaxRate(taxId, newRate) {
  const response = await fetch(`/api/v0/taxes/${taxId}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'x-client-key': 'your-client-key',
      'x-client-secret': 'your-client-secret'
    },
    body: JSON.stringify({
      id: taxId,
      percentage: newRate
    })
  });

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

  return response.json();
}

// Usage
await updateTaxRate('123e4567-e89b-12d3-a456-426614174000', 21.0);

2. Tax Status Management

Activate or deactivate taxes:

async function toggleTaxStatus(taxId, isActive) {
  const response = await fetch(`/api/v0/taxes/${taxId}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'x-client-key': 'your-client-key',
      'x-client-secret': 'your-client-secret'
    },
    body: JSON.stringify({
      id: taxId,
      active: isActive
    })
  });

  const updatedTax = await response.json();
  console.log(`Tax "${updatedTax.name}" is now ${isActive ? 'active' : 'inactive'}`);
  
  return updatedTax;
}

3. Application Association Management

Add or remove applications from tax access:

async function updateTaxApplications(taxId, appIds) {
  const response = await fetch(`/api/v0/taxes/${taxId}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'x-client-key': 'your-client-key',
      'x-client-secret': 'your-client-secret'
    },
    body: JSON.stringify({
      id: taxId,
      appIds: appIds
    })
  });

  const updatedTax = await response.json();
  console.log(`Tax now associated with ${updatedTax.apps.length} applications`);
  
  return updatedTax;
}

4. Bulk Tax Updates

Update multiple fields at once:

async function updateTaxConfiguration(taxId, updates) {
  const updatePayload = {
    id: taxId,
    ...updates
  };

  const response = await fetch(`/api/v0/taxes/${taxId}`, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json',
      'x-client-key': 'your-client-key',
      'x-client-secret': 'your-client-secret'
    },
    body: JSON.stringify(updatePayload)
  });

  return response.json();
}

// Usage
await updateTaxConfiguration('123e4567-e89b-12d3-a456-426614174000', {
  name: 'Updated Tax Name',
  percentage: 22.5,
  description: 'New tax configuration for 2024',
  active: true
});

Integration ExamplesCopied!

React Component

import React, { useState } from 'react';

function TaxEditor({ taxId, initialData, onUpdate }) {
  const [formData, setFormData] = useState({
    name: initialData.name || '',
    description: initialData.description || '',
    percentage: initialData.percentage || 0,
    active: initialData.active || true
  });
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setIsLoading(true);
    setError(null);

    try {
      const response = await fetch(`/api/v0/taxes/${taxId}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
          'x-client-key': process.env.REACT_APP_CLIENT_KEY,
          'x-client-secret': process.env.REACT_APP_CLIENT_SECRET
        },
        body: JSON.stringify({
          id: taxId,
          ...formData
        })
      });

      if (!response.ok) {
        throw new Error(`Update failed: ${response.statusText}`);
      }

      const updatedTax = await response.json();
      onUpdate(updatedTax);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  const handleChange = (field, value) => {
    setFormData(prev => ({
      ...prev,
      [field]: value
    }));
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Tax Name:</label>
        <input
          type="text"
          value={formData.name}
          onChange={(e) => handleChange('name', e.target.value)}
          disabled={isLoading}
        />
      </div>

      <div>
        <label>Description:</label>
        <textarea
          value={formData.description}
          onChange={(e) => handleChange('description', e.target.value)}
          disabled={isLoading}
        />
      </div>

      <div>
        <label>Percentage:</label>
        <input
          type="number"
          step="0.01"
          value={formData.percentage}
          onChange={(e) => handleChange('percentage', parseFloat(e.target.value))}
          disabled={isLoading}
        />
      </div>

      <div>
        <label>
          <input
            type="checkbox"
            checked={formData.active}
            onChange={(e) => handleChange('active', e.target.checked)}
            disabled={isLoading}
          />
          Active
        </label>
      </div>

      {error && <div className="error">{error}</div>}

      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Updating...' : 'Update Tax'}
      </button>
    </form>
  );
}

Python Service

import requests
from typing import Dict, Any, Optional

class TaxUpdateService:
    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 {
            'Content-Type': 'application/json',
            'x-client-key': self.client_key,
            'x-client-secret': self.client_secret
        }
    
    def update_tax(self, tax_id: str, updates: Dict[str, Any]) -> Dict[str, Any]:
        """Update a tax configuration"""
        payload = {
            'id': tax_id,
            **updates
        }
        
        response = requests.put(
            f"{self.base_url}/api/v0/taxes/{tax_id}",
            json=payload,
            headers=self._get_headers()
        )
        
        response.raise_for_status()
        return response.json()
    
    def update_tax_rate(self, tax_id: str, new_rate: float) -> Dict[str, Any]:
        """Update only the tax rate"""
        return self.update_tax(tax_id, {'percentage': new_rate})
    
    def toggle_tax_status(self, tax_id: str, active: bool) -> Dict[str, Any]:
        """Activate or deactivate a tax"""
        return self.update_tax(tax_id, {'active': active})
    
    def update_tax_applications(self, tax_id: str, app_ids: list) -> Dict[str, Any]:
        """Update the applications associated with a tax"""
        return self.update_tax(tax_id, {'appIds': app_ids})

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

# Update tax rate
updated_tax = tax_service.update_tax_rate(
    '123e4567-e89b-12d3-a456-426614174000', 
    21.0
)

# Update multiple fields
updated_tax = tax_service.update_tax(
    '123e4567-e89b-12d3-a456-426614174000',
    {
        'name': 'New VAT Rate',
        'percentage': 19.5,
        'description': 'Updated VAT rate for 2024'
    }
)

Node.js/Express

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

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

  async updateTax(taxId, updates) {
    const payload = {
      id: taxId,
      ...updates
    };

    try {
      const response = await axios.put(
        `${this.baseUrl}/api/v0/taxes/${taxId}`,
        payload,
        {
          headers: {
            'Content-Type': 'application/json',
            'x-client-key': this.clientKey,
            'x-client-secret': this.clientSecret
          }
        }
      );

      return response.data;
    } catch (error) {
      if (error.response) {
        throw new Error(`Update failed: ${error.response.data.message}`);
      }
      throw error;
    }
  }

  async validateAndUpdateTax(taxId, updates) {
    // Validate updates before sending
    if (updates.percentage !== undefined && updates.percentage < 0) {
      throw new Error('Tax percentage cannot be negative');
    }

    if (updates.name !== undefined && updates.name.trim() === '') {
      throw new Error('Tax name cannot be empty');
    }

    return this.updateTax(taxId, updates);
  }
}

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

app.put('/taxes/:id', async (req, res) => {
  try {
    const updatedTax = await taxManager.validateAndUpdateTax(
      req.params.id,
      req.body
    );
    
    res.json(updatedTax);
  } catch (error) {
    console.error('Tax update error:', error);
    res.status(400).json({ error: error.message });
  }
});

// Specific update endpoints
app.patch('/taxes/:id/rate', async (req, res) => {
  try {
    const { percentage } = req.body;
    const updatedTax = await taxManager.updateTax(req.params.id, { percentage });
    res.json(updatedTax);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

app.patch('/taxes/:id/status', async (req, res) => {
  try {
    const { active } = req.body;
    const updatedTax = await taxManager.updateTax(req.params.id, { active });
    res.json(updatedTax);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Validation RulesCopied!

ID Validation

  • Path parameter and body id must match

  • Must be a valid UUID v4 format

  • Tax must exist and be accessible to your application

Field Validation

  • name: Optional string, no specific length restrictions

  • description: Optional string, can be null

  • percentage: Optional number, can include decimals

  • active: Optional boolean

  • appIds: Optional array of valid UUID v4 strings

Business Rules

  • Cannot update taxes that your application doesn't have access to

  • Your application ID is automatically included in appIds

  • All specified application IDs must belong to the same business

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. Validate Before Update

Always validate data before sending updates:

function validateTaxUpdate(updates) {
  const errors = [];
  
  if (updates.percentage !== undefined) {
    if (updates.percentage < 0) errors.push('Percentage cannot be negative');
    if (updates.percentage > 100) errors.push('Percentage seems unusually high');
  }
  
  if (updates.name !== undefined && updates.name.trim() === '') {
    errors.push('Name cannot be empty');
  }
  
  if (errors.length > 0) {
    throw new Error(errors.join(', '));
  }
}

2. Partial Updates

Only send fields that actually changed:

function createUpdatePayload(originalTax, formData) {
  const updates = { id: originalTax.id };
  
  Object.keys(formData).forEach(key => {
    if (formData[key] !== originalTax[key]) {
      updates[key] = formData[key];
    }
  });
  
  return updates;
}

3. Error Recovery

Implement proper error handling and recovery:

async function updateTaxWithRetry(taxId, updates, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await updateTax(taxId, updates);
    } catch (error) {
      if (attempt === maxRetries || error.status === 404 || error.status === 401) {
        throw error;
      }
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
}

4. Audit Logging

Log important tax changes for compliance:

async function updateTaxWithAudit(taxId, updates, userId) {
  const originalTax = await fetchTax(taxId);
  const updatedTax = await updateTax(taxId, updates);
  
  // Log the change
  await logAuditEvent({
    action: 'TAX_UPDATE',
    userId: userId,
    entityId: taxId,
    changes: {
      before: originalTax,
      after: updatedTax,
      fields: Object.keys(updates)
    },
    timestamp: new Date().toISOString()
  });
  
  return updatedTax;
}

Security ConsiderationsCopied!

  • Access Control: Strict application-level access control prevents unauthorized modifications

  • Data Validation: All input is validated to prevent malicious data

  • Business Logic: Automatic inclusion of current app ID prevents loss of access

  • Audit Trail: Consider logging all tax modifications for compliance

  • Rate Limiting: Prevents abuse while allowing legitimate updates