VAT Number Validation with n8n
Validate a VAT number in n8n with its built-in HTTP Request node, then send valid numbers, invalid numbers, and failed checks to separate branches. This guide starts with a manual run so you can inspect each result before connecting a form, CRM, or scheduled trigger.
You need an n8n workspace and an Avatcado test API key. Create a free Avatcado account, then create a test key in the dashboard. The examples use avat_test_your_api_key as a placeholder. Replace it with your key when saving the credential.
1. Start with one test VAT number
Create a workflow with a Manual Trigger. Connect an Edit Fields (Set) node and name it Edit Fields. Set these values:
| Setting | Value |
|---|---|
| Mode | Manual Mapping |
| Fields to Set: Name | vat_number |
| Type | String |
| Value, in Fixed mode | DE111111111 |
| Include Other Input Fields | Off |
This number produces a valid result with an Avatcado test key. Test requests use predictable fixtures and do not consume your monthly validation quota. The same number with a live key makes a real lookup, so keep the test credential selected throughout this walkthrough.
2. Save the API key as a credential
Connect an HTTP Request node. Set Authentication to Generic Credential Type and Generic Auth Type to Header Auth. Create a new credential named Avatcado test:
| Credential field | Value |
|---|---|
| Name | Authorization |
| Value | Bearer avat_test_your_api_key |
Include the space after Bearer. Save the credential and select it in HTTP Request. Keep the key in this credential; the VAT number belongs in workflow data. n8n documents the fields in its Header Auth reference.
3. Configure the HTTP Request node
Enter the endpoint without a query string. Add the VAT number through Query Parameters so n8n encodes the value. Switch the query value to Expression mode and paste the expression including its braces.
| Setting | Value |
|---|---|
| Method | GET |
| URL | https://api.avatcado.com/v1/validate |
| Authentication | Generic Credential Type |
| Generic Auth Type | Header Auth |
| Credential for Header Auth | Avatcado test |
| Send Query Parameters | On |
| Specify Query Parameters | Using Fields Below |
| Query parameter Name | vat_number |
| Query parameter Value, Expression mode | {{ $json.vat_number }} |
| Send Body | Off |
| Options > Response > Response Format | JSON |
| Options > Response > Include Response Headers and Status | On |
| Options > Response > Never Error | On |
| Options > Timeout | 60000 milliseconds |
| Settings > Retry On Fail | Off for this walkthrough |
| Settings > On Error | Continue (using error output) |
The Response options matter here. Include Response Headers and Status puts the API JSON under body and exposes statusCode and headers beside it. Never Error lets HTTP 4xx and 5xx responses reach the regular output for routing. Connection failures and response-parsing failures still use the error output. See n8n's HTTP Request response options.
Execute the workflow and open the HTTP Request output in JSON view. These selected fields show the shape you will use downstream:
{
"body": {
"data": {
"valid": true,
"vat_number": "DE111111111",
"country_code": "DE",
"company": {
"name": "Test GmbH",
"address": "Berlin, Germany"
},
"requested_at": "2026-09-22T10:00:00.000Z"
},
"meta": {
"request_id": "example-request-id",
"mode": "test"
}
},
"headers": { "content-type": "application/json" },
"statusCode": 200
}With these settings, use {{ $json.body.data.valid }}. Turning off the full-response option changes that path to {{ $json.data.valid }} and removes the status check used below.
4. Route valid, invalid, and error results
Connect the HTTP Request regular output to a Switch node. Set Mode to Expression, Number of Outputs to 3, and paste this into Output Index in Expression mode:
{{ $json.statusCode === 200 && !$json.body?.error
? ($json.body?.data?.valid === true
? 0
: ($json.body?.data?.valid === false ? 1 : 2))
: 2 }}This expression requires a successful HTTP response and a boolean validation result. Missing fields or a string such as "false" go to the error branch. A successful check with valid: false goes to the invalid branch.
| Output | Connect to an Edit Fields node named | Meaning |
|---|---|---|
| 0 | Valid | HTTP 200 and body.data.valid === true |
| 1 | Invalid | HTTP 200 and body.data.valid === false |
| 2 | Error | HTTP error or unexpected response |
Also connect the HTTP Request error output directly to the same Error node. A network failure has no usable API response for the Switch. This second connection ensures it still reaches the error branch. The output-index settings are described in n8n's Switch reference.
For the first run, set all three final Edit Fields nodes to Manual Mapping and add a String field named result. Give it the fixed value valid, invalid, or error to match the branch. These nodes let you inspect routing without updating another app.
5. Map fields without losing missing-company cases
A valid result can have company: null. Add a String field named company_name to the Valid node, using {{ $json.body.data.company?.name ?? '' }} in Expression mode. The fallback keeps the value empty when the source supplies no name. Preserve an existing CRM name when this value is empty.
| Field to inspect or store | Expression after the Switch |
|---|---|
| VAT number | {{ $json.body.data.vat_number }} |
| Company name | {{ $json.body.data.company?.name ?? '' }} |
| Company address | {{ $json.body.data.company?.address ?? '' }} |
| Check timestamp | {{ $json.body.data.requested_at }} |
| Request ID | {{ $json.body.meta?.request_id ?? '' }} |
| Source status | {{ $json.body.meta?.source_status ?? '' }} |
| Stale result | {{ $json.body.meta?.stale === true }} |
Use these success fields on the Valid and Invalid branches. Keep the source metadata alongside the result; your workflow may need a review step for a stale response. If a later Edit Fields node drops input fields, map everything you need in that node or enable Include Other Input Fields.
On the Error node, add a String field named code with {{ $json.body?.error?.code ?? 'network_or_response_error' }}. An HTTP error carries its details under body.error. A network or parsing error comes from n8n, so inspect the error output in the execution instead of expecting Avatcado's envelope.
6. Test every branch
Change the first Edit Fields node's vat_number and execute the entire workflow for each row. Keep the Avatcado test credential selected. Unpin any node data so each run makes a new request.
| Input | Expected HTTP status | Expected branch |
|---|---|---|
DE111111111 | 200 | Valid, company name Test GmbH |
DE000000000 | 200 | Invalid |
DE222222222 | 200 | Valid, empty company name |
DE123 | 422 | Error, invalid_vat_format |
DE999999999 | 503 | Error, upstream_unavailable |
DE777777777 | 429 | Error, rate_limit_exceeded |
To check the network-error connection, duplicate the workflow and temporarily use the URL https://avatcado-network-test.invalid/v1/validate with Authentication set to None. Run it once. The HTTP Request error output should reach Error, and Switch should remain unexecuted. Restore the API URL and credential before continuing. The .invalid address is only for this deliberate failure test.
The workflow settings and expressions were executed with n8n 2.40.5 against an isolated fixture server, including a connection reset. The fixture tests checked request authentication, all three routes, and null company fields. The table above uses Avatcado's documented test-mode numbers.
Handle failed checks before adding downstream actions
- 422: Ask for a corrected VAT number. Retrying the same input repeats the failure.
- 401 or 403: Inspect the selected credential and access permissions before retrying.
- 429: Inspect
body.error.codeandheaders["retry-after"]when present. A burst limit calls for slower requests; a monthly quota needs available quota or a plan change. - 503 or network failure: Keep the record pending and schedule a bounded retry or manual review. Record the request ID when an API response supplies one.
Never Error keeps HTTP failures on the regular output, so Retry On Fail alone will not retry a 429 or 503 in this configuration. Implement any delayed retries from the Error branch with a maximum attempt count. Leave that branch visible in your operations process; continuing a workflow can make a handled failure appear as a successful execution.
Switch to live validation
Create a separate Header Auth credential named Avatcado live with your actual avat_live_ key. Select it in HTTP Request, keep the same endpoint, and replace the fixture VAT number with a real number supplied by your customer. Test the complete workflow once before replacing Manual Trigger with your production trigger.
Map that trigger's VAT field into vat_number in the first Edit Fields node. Retain a record identifier if later steps update your CRM, and decide what each branch should do before enabling those writes. A VAT validation result is one input to your invoicing decision; configure tax treatment separately.
For field choices and record updates, read the CRM validation guide. The Make guide and Zapier guide cover the same API call in those tools. The API documentation has the full response and error reference.
Sources
- n8n HTTP Request node n8n, accessed September 22, 2026
- n8n Switch node n8n, accessed September 22, 2026
- Avatcado test mode Avatcado, accessed September 22, 2026
Related guides
VAT Validation with Make
How to validate VAT numbers in a Make scenario using the HTTP module and the Avatcado REST API. No code required.
VAT Validation with Zapier
How to validate VAT numbers in a Zapier workflow using the Webhooks by Zapier action and the Avatcado REST API. No code required.
Validate VAT Numbers in Your CRM
How to add real-time VAT number validation to CRM account creation and lead qualification workflows. Covers Salesforce, HubSpot, and no-code automation tools.