Skip to main content
A tax configuration tells Spotflow to add VAT, a levy, or a flat duty to what you collect in one region. You set it up once per region, and it applies from then on. This guide finds your region, starts from a ready-made template, creates the configuration, changes the rate, and switches it off. All calls are on the gateway service:

What you’ll need

  • A dashboard token (JWT). Not your secret key — see the warning below.
  • A region you are set up to collect in.
These endpoints reject the secret key. Unlike the rest of the API, tax configuration takes a dashboard token only. A secret key returns:
Read that message carefully — it says your token is malformed, but the real cause is usually that you sent a perfectly valid secret key to an endpoint that does not take one.

The shape of the flow

1

Find your region id

Every call here is scoped to one region.
2

Look at the templates

Spotflow ships the standard rate for each region.
3

Create the configuration

Adopt a template, or set your own rate.
4

Change the rate

Update it without recreating it.
5

Switch it off

Deactivate rather than delete.

Step 1: Find your region id

Every tax call needs a regionId, and it is a small integer, not a UUID.
id is the regionId you need. taxStatus tells you whether tax is switched on for that region yet.
GET /merchant/regions/tax-status returns a narrower list — only the regions that support tax at all. On our test merchant that was Ghana, Nigeria, South Africa and Kenya, while /merchant/regions returned those plus Benin, Cameroon, Côte d’Ivoire and Global. If a region is missing from the tax-status list, you cannot configure tax there.

Step 2: Look at the templates

Spotflow ships the standard rate for each region so you do not have to look it up.
For Ghana:
template: true means this is not yours yet. It is a suggestion, it has a templateId but no id, and nothing is being charged. Step 3 turns it into a real configuration.
regionId is required. Leaving it off returns 400:
A region with no template and no configuration returns [] — for example regionId=3 (South Africa).

Step 3: Create the configuration

template is now false and there is an id. That is a live configuration, and tax is now being applied to collections in that region. Creating the first configuration also enables tax for the merchant-region automatically — you do not switch it on separately.
The id is an integer (32), not a UUID, unlike accounts, transfers and plans. Same for regionId and templateId.

capAmount is required for percentage tax

The API specification marks capAmount as optional. It is not, for percentage type. Leaving it out returns:
This check runs before the rate check, so an invalid rate with a missing cap reports only the cap. Fix the cap first, then you will see the rate error:
flat type needs no capAmount.

The template disappears once you create a configuration

List the region again after creating:
The template: true row is gone. This endpoint returns templates and configurations mixed together, and a template stops being offered once you have adopted it. Always check the template flag rather than assuming a row is live.
A region can hold more than one configuration — we created a second, flat Ghana VAT 2 alongside this one and it was accepted with 201. The specification suggests a duplicate is rejected; on dev it is not. Check the list before creating, or you can end up charging two taxes in one region.

Step 4: Change the rate

There is no regionId in the update body. A configuration’s region is fixed when you create it. To move a tax to a different region, deactivate this one and create a new one.
taxName, taxType and rate are all required on the update, so send the full object even when changing one number. To read a single configuration:

Step 5: Switch it off

There is no delete. You deactivate.
Sending { "status": "active" } switches it back on, with the same rate and cap intact. Deactivating is how you stop charging a tax while keeping the record for your own history.
The status values are lowercase — active and inactive — even though the API specification lists them uppercase.

When things go wrong

What to do next