Skip to content
Loading keys…

Tax

Enable tax collection, calculate it, and manage your registrations.

Tax turns a rate table and a set of registrations into an amount on every payment. Once you switch it on, each transaction is taxed and recorded automatically, and a tax object appears on payment and refund responses. Until then, these endpoints let you look up rates and try calculations without committing to anything.

Settings

GEThttps://api.wajub.com/tax/settings
curl https://api.wajub.com/tax/settings \
-H "Authorization: $WAJUB_API_KEY"

The configuration comes back under tax:

Response · 200 OK
{
"code": 200,
"status": "OK",
"tax": {
"enabled": false,
"inclusive": false,
"default_country": "CM",
"tax_id_type": null,
"tax_registration_number": null
}
}
enabledbooleanoptional
Whether tax is calculated and recorded on every transaction.
inclusivebooleanoptional
true means the amount you send already contains the tax; false means tax is added on top.
default_countrystringoptional
The country whose rate applies when a transaction carries none.
tax_id_typestringoptional
The kind of registration number you hold, such as a VAT number.
tax_registration_numberstringoptional
Your own registration number, printed on receipts and invoices.

Turning tax on is a PUT on the same path. enabled: true makes tax_id_type and registration_number mandatory in the same request, and the number is checked against the format expected for its type.

curl -X PUT https://api.wajub.com/tax/settings \
-H "Authorization: $WAJUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "enabled": true,
  "inclusive": true,
  "default_country": "CM",
  "tax_id_type": "vat",
  "registration_number": "M071812345678A"
}'

Calculating before charging

POST /tax/calculate answers the question "what will this cost with tax" without creating anything. Use it to show a total at checkout.

POSThttps://api.wajub.com/tax/calculate
amountnumberrequired
Amount in the major unit, at least 0.01.
countrystringrequired
ISO 3166-1 alpha-2 country whose rate applies.
currencystringoptionaldefault : XAF
ISO 4217 code.
tax_inclusivebooleanoptionaldefault : false
Whether amount already contains the tax.
customer_idstringoptional
A customer uid. Their exemptions and reverse-charge status are then applied.
tax_codestringoptional
A product tax code, for goods taxed differently from the standard rate. Ignored when the customer is exempt or under reverse charge.
curl https://api.wajub.com/tax/calculate \
-H "Authorization: $WAJUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "amount": 25000,
  "currency": "XAF",
  "country": "CM",
  "customer_id": "cus_01JXXXXXXXXXXXXX"
}'

total is the figure to show the customer, and the figure to pass as amount when you create the payment:

Response · 200 OK
{
"code": 200,
"status": "OK",
"calculation": {
"amount": 25000,
"tax_amount": 4812.5,
"taxable_amount": 25000,
"total": 29812.5,
"rate": 19.25,
"tax_name": "TVA",
"country_code": "CM",
"currency": "XAF",
"tax_inclusive": false,
"tax_code": null,
"customer_exempt": false,
"reverse_charge": false
}
}

Exempt and reverse charge are answers, not errors

When customer_exempt or reverse_charge is true, tax_amount is zero and total equals amount. That is a correct calculation for a customer who holds a verified tax ID in a reverse-charge jurisdiction, not a failure to calculate.

Reference data

Read-only lists, identical for every merchant. None of them accepts a cursor; the two that paginate use per_page, default 50 and maximum 100.

EndpointReturns underFiltersPaginated
GET /tax/ratesratescountryNo
GET /tax/codestax_codescategoryYes
GET /tax/codes/{code}tax_coden/a
GET /tax/jurisdictionsjurisdictionscountry, state, typeYes
GET /tax/thresholdsthresholdscountryNo

GET /tax/rates is the shortest of them, one standard rate per country:

Response · 200 OK
{
"code": 200,
"status": "OK",
"rates": [
{
"country_code": "CM",
"name": "TVA",
"rate": 19.25,
"type": "standard"
},
{
"country_code": "CI",
"name": "TVA",
"rate": 18,
"type": "standard"
}
],
"total": 2
}

jurisdiction_type on a jurisdiction is one of country, state, county, city or district, and is_compound says whether its rate stacks on top of a parent's rather than replacing it.

GET /tax/codes/{code} returns one code plus every country rate attached to it, under rates, so a single call tells you how a product category is taxed everywhere you sell.

Registrations

Where you are registered to collect tax. Five endpoints, the usual shape, all scoped to your team.

MethodEndpointReturns
GET/tax/registrationsregistrations
POST/tax/registrationsregistration, 201
GET/tax/registrations/{id}registration
PUT/tax/registrations/{id}registration
DELETE/tax/registrations/{id}confirmation
country_codestringrequired
ISO 3166-1 alpha-2.
state_codestringoptional
For countries that tax below national level.
typestringoptional
One of standard, simplified, ioss, oss.
registration_numberstringoptional
Your number in that jurisdiction.
registered_atdateoptional
When the registration took effect.
expires_atdateoptional
Must be after registered_at.

A new registration is created with status: "active".

Thresholds and alerts

Many jurisdictions only require registration past a turnover threshold. GET /tax/thresholds lists them; GET /tax/thresholds/alerts compares them against your own volume and returns the countries where you are approaching or already past the line.

curl https://api.wajub.com/tax/thresholds/alerts \
-H "Authorization: $WAJUB_API_KEY"

Customer tax IDs

A customer's own tax IDs live under the customer, not under /tax. Adding one queues an asynchronous verification, so the ID comes back with verification_status: "pending" and settles later.

MethodEndpointReturns
GET/customers/{customer_id}/tax_idstax_ids
POST/customers/{customer_id}/tax_idstax_id, 201
DELETE/customers/{customer_id}/tax_ids/{id}confirmation
typestringrequired
The kind of identifier, up to 64 characters.
valuestringrequired
The identifier itself, up to 128 characters.
country_codestringrequired
ISO 3166-1 alpha-2.

Tax IDs change the calculation

A verified tax ID is what makes customer_exempt or reverse_charge come back true from POST /tax/calculate. Add the ID before you calculate, not after.

Reports

GET /tax/reports aggregates tax collected over a period. It accepts period, one of 7d, 30d, 90d, month, year or custom, plus start_date and end_date for custom, and optional currency and country filters. The result comes back under report.

What did you think of this content?