The Perl Advent Calendar needs more articles for 2022. Submit your idea today!


Business::Tax::Avalara - An interface to Avalara's REST webservice


        use Business::Tax::Avalara;
        my $avalara_gateway = Business::Tax::Avalara->new(
                customer_code  => $customer_code,
                company_code   => $company_code,
                user_name      => $user_name,
                password       => $password,
                origin_address =>
                        line_1      => '1313 Mockingbird Lane',
                        postal_code => '98765',
        my $tax_results = $avalara_gateway->get_tax(
                destination_address =>
                        line_1      => '42 Evergreen Terrace',
                        city        => 'Springfield',
                        postal_code => '12345',
                cart_lines =>
                                sku      => '42ACE',
                                quantity => 1,
                                amount   => '8.99',
                                sku      => '9FCE2',
                                quantity => 2,
                                amount   => '38.98',


Business::Tax::Avalara is a simple interface to Avalara's REST-based sales tax webservice. It takes in a perl hash of data to send to Avalara, generates the JSON, fetches a response, and converts that back into a perl hash structure.

This module only supports the 'get_tax' method at the moment.


Version 1.2.0



Creates a new Business::Tax::Avalara object with various options that do not change between requests.

        my $avalara_gateway = Business::Tax::Avalara->new(
                customer_code   => $customer_code,
                company_code    => $company_code,
                user_name       => $user_name
                pasword         => $password,
                is_development  => boolean (optional), default 0
                origin_address  => $origin_address (optional),
                memcached       => A Cache::Memcached or Cache::Memcached::Fast object.
                request_timeout => Request timeout in seconds. Default is 3.
                debug           => 0,

The fields customer_code, company_code, user_name, and password should be provided by your Avalara representative. Account number and License key are synonyms for user_name and password, respectively.

is_development should be set to 1 to use the development URL, and 0 for production uses.

origin_address can either be set here, or passed into get_tax, depending on if it changes per request, or if you're always shipping from the same location. It is a hash ref, see below for formatting details.

If a memcached object is passed in, we can use this so that we don't send the same request over in a certain period of time. This combines below with 'cache_timespan' and 'unique_key' in the get_tax() call.

If debug is set to a true value, it will dump out the raw json messages being sent to and coming back from Avalara.

Returns a Business::Tax::Avalara object.


Makes a JSON request using the 'get_tax' method, parses the response, and returns a perl hash.

        my $tax_results = $avalara_gateway->get_tax(
                destination_address   => $address_hash,
                origin_address        => $address_hash (may be specified in new),
                document_date         => $date (optional), default is current date
                cart_lines            => $cart_line_hash,
                customer_usage_type   => $customer_usage_type (optional),
                discount              => $order_level_discount (optional),
                purchase_order_number => $purchase_order_number (optional),
                exemption_number      => $exemption_number (optional),
                detail_level          => $detail_level (optional), default 'Tax',
                document_type         => $document_type (optional), default 'SalesOrder'
                document_code         => $document_code (optional), a unique identifier
                payment_date          => $date (optional),
                reference_code        => $reference_code (optional),
                commit                => 1|0, # Default 0, whether this is a 'final' query.
                unique_key            => A unique key for memcache (optional, see below)
                cache_timespan        => The number of seconds to cache results (see below),
                currency_code         => 3 character ISO 4217 compliant currency code (optional),

If you are issuing a refund or credit memo for part of the order (for the full order you may want to void the order using the cancel_tax() method), you may need to specify the date the tax was originally calculated, and you need to specify the amounts as negative values.

        my $tax_results = $avalara_gateway->get_tax(
                document_type         => 'ReturnInvoice',
                document_code         => $original_order_number (optional), or a unique identifier
                document_date         => $date (optional), default is current date
                tax_override          => {
                        reason            => 'Return',
                        tax_override_type => 'TaxDate',
                        tax_date          => 'YYYY-MM-DD', # Original date of the order.
                # Just the lines being refunded.
                cart_lines            => [
                                sku      => $sku_identifier,
                                quantity => $number_of_units_to_refund,
                                amount   => $amount_to_refund,  # Negative value.
                discount              => $amount_to_decrease_refund (optional),  # Negative value.
                commit                => 1|0, # Default 0, whether this is a 'final' query.
                # Include other fields as needed.

See below for the definitions of address and cart_line fields. The field origin_address may be specified here if it changes between transactions, or in new if it's largely static.

detail level is one of 'Tax', 'Summary', 'Document', 'Line', or 'Diagnostic'. See the Avalara documentation for the distinctions.

document_type is one of 'SalesOrder', 'SalesInvoice', 'PurchaseOrder', 'PurchaseInvoice', 'ReturnOrder', and 'ReturnInvoice'.

document_code is optional, but highly recommended. If you do not include this, Avalara will generate a new internal unique id for each request, and it does not associate the commits to any queries you made along the way.

If cache_timespan is set and you passed a memcached object into new(), it will attempt to cache the result based on the unique key passed in.

Returns a perl hashref based on the Avalara return. See the Avalara documentation for the full description of the output, but the highlights are:

                ResultCode     => 'Success',
                TaxAddresses   => [ array of address information ],
                TaxDate        => Date,
                TaxLines       =>
                        LineNumber => # The value of the line number
                                Discount      => Discount,
                                LineNo        => Line Number passed in,
                                Rate          => Tax rate used,
                                Tax           => Line item tax
                                Taxability    => "true" or "false",
                                Taxable       => Amount taxable,
                                TaxCalculated => Line item tax
                                TaxCode       => Tax Code used in the calculation
                                Tax Details   => Details about state, county, city components of the tax
                Timestamp      => Timestamp,
                TotalAmount    => Total amount before tax
                TotalDiscount  => Total Discount
                TotalExemption => Total amount exempt
                TotalTax       => Tax for the whole order
                TotalTaxable   => Amount that's taxable


Makes a JSON request using the 'cancel_tax' method, parses the response, and returns a perl hash.

        my $tax_results = $avalara_gateway->cancel_tax(
                document_type => $document_type, default 'SalesOrder'
                doc_code      => $doc_code,
                cancel_code   => $cancel_code, default 'DocVoided',
                doc_id        => $doc_id,

Either doc_id (which is Avalara's transaction ID returned from get_tax() ) or the combination of document_type, doc_coe, and doc_id are required.

Returns a perl hashref based on the Avalara return. See the Avalara documentation for the full description of the output, but the highlights are:

        'CancelTaxResult' =>
                ResultCode    => 'Success',
                DocID         => SomeDocID,
                TransactionID => Avalara's ID,



Generates the json to send to Avalara's web service.

Returns a JSON object.


Generates the json to cancel a tax request to Avalara's web service.

Returns a JSON object.


Given an address hashref, generates and returns a data structure to be converted to JSON.

An address hashref is defined as:

        my $address = {
                line_1        => $first_line_of_address,
                line_2        => $second_line_of_address,
                line_3        => $third_line_of_address,
                city          => $city,
                region        => $state_or_province,
                country       => $iso_2_code,
                postal_code   => $postal_or_ZIP_code,
                latitude      => $latitude,
                longitude     => $longitude,
                tax_region_id => $tax_region_id,

All fields are optional, though without enough to identify an address, your results will be less than satisfying.

Country coes are ISO 3166-1 (alpha 2) format, such as 'US'.


Generates a data structure from a cart_line hashref. Cart lines are:

        my $cart_line = {
                'line_number'         => $number (optional, will be generated if omitted.),
                'item_code'           => $item_code
                'sku'                 => $sku, # Use sku OR item_code
                'tax_code'            => $tax_code,
                'customer_usage_type' => $customer_usage_code
                'description'         => $description,
                'quantity'            => $quantity,
                'amount'              => $amount, # Extended price, ie, price * quantity
                'discounted'          => $is_included_in_discount, # Boolean (True or False)
                'tax_included'        => $is_tax_included, # Boolean (True or False)
                'ref_1'               => $reference_1,
                'ref_2'               => $reference_2,

One of item_code or sku, quantity, and amount are required fields.

Customer usage type determines the type of item (sometimes called entity or use code). In some states, different types of items have different tax rates.


Makes the https request to Avalara, and returns the response json.


Converts the returned JSON into a perl hash.


Return the database handle tied to the audit object.

        my $memcache = $avalara_gateway->get_memcache();


Get a value from the cache.

        my $value = $avalara_gateway->get_cache( key => $key );


Set a value into the cache.

                key         => $key,
                value       => $value,
                expire_time => $expire_time,


Kate Kirby, <kate at>.


Nathan Gray <>


Please report any bugs or feature requests to bug-business-tax-avalara at, or through the web interface at I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.


You can find documentation for this module with the perldoc command.

        perldoc Business::Tax::Avalara

You can also look for information at:


Thanks to ThinkGeek ( and its corporate overlords at Geeknet (, for footing the bill while we eat pizza and write code for them!


Copyright 2012 Kate Kirby.

Copyright (C) 2017 Nathan Gray

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see