REST API Labs
Check EU VAT numbers from your backend, over plain HTTP
One route: POST /api/v1/vat/verify. Send up to 20 VAT numbers; each row comes back valid, invalid or unavailable from the EU VIES register. Two more checks have a route, listed below; everything else is MCP.
Labs. The route is tested in our suite; it has not yet run against the real register on this website, and nobody has been charged through it.
1 · A person creates the key
Sign in, open Account → Connection, create a connection and copy its secret (
jxc_live_…, shown once). The same page revokes it.2 · Your backend calls
Send it as
Authorization: Bearer. No browser, no MCP client. The person approves; the backend or agent runs.3 · You pay per answer
1 credits (EUR 0.01) for each row VIES answered. A row VIES did not answer (down, busy, timed out) costs 0 and is listed for a retry.
New workspaces get 100 free VAT checks against the EU VIES register, once per workspace, no card.
curl
# requesterVatId: YOUR OWN EU VAT number (optional) — a valid row then carries the EU's consultation number, on your name
curl -s -X POST https://jithox.com/api/v1/vat/verify \
-H "authorization: Bearer $JITHOX_CONNECTION_SECRET" \
-H 'content-type: application/json' \
-H 'x-jithox-idempotency-key: invoice-2026-0917-C-001' \
-d '{"rows":[{"reference":"C-001","vatId":"BE0403170701"}],"requesterVatId":"BE0417497106"}'C# (.NET 8, HttpClient)
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
// The connection secret (jxc_live_…) that a person created at https://jithox.com/mcp/account#connection
var secret = Environment.GetEnvironmentVariable("JITHOX_CONNECTION_SECRET")
?? throw new InvalidOperationException("Set JITHOX_CONNECTION_SECRET");
using var http = new HttpClient { BaseAddress = new Uri("https://jithox.com"), Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", secret);
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/vat/verify")
{
Content = JsonContent.Create(new
{
rows = new[] { new { reference = "C-001", vatId = "BE0403170701" } },
// Optional: YOUR OWN VAT number with its country prefix (e.g. BE0417497106). Every valid row then carries the
// European Commission's VIES consultation number, in your name. Empty or null = no number.
requesterVatId = Environment.GetEnvironmentVariable("JITHOX_REQUESTER_VAT_ID"),
}),
};
// Optional: the same key with the same rows is charged once.
request.Headers.Add("x-jithox-idempotency-key", "invoice-2026-0917-C-001");
using var response = await http.SendAsync(request);
using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var root = doc.RootElement;
switch ((int)response.StatusCode)
{
case 200:
foreach (var row in root.GetProperty("result").GetProperty("rows").EnumerateArray())
{
var verdict = row.GetProperty("verdict").GetString(); // valid | invalid | unavailable | not_checked
Console.WriteLine($"{row.GetProperty("input").GetString()}: {verdict} — {row.GetProperty("detail").GetString()}");
// "unavailable" is a VIES outage, not a verdict on the number: send it again later, it cost nothing.
if (row.TryGetProperty("consultationNumber", out var nr)) // only on valid, only with requesterVatId
Console.WriteLine($" VIES consultation number {nr.GetString()} in the name of {row.GetProperty("consultationRequester").GetString()}");
}
var consultation = root.GetProperty("consultation");
if (!consultation.GetProperty("available").GetBoolean())
Console.WriteLine(consultation.GetProperty("note").GetString()); // e.g. not_requested: no requesterVatId was sent
var billing = root.GetProperty("billing");
Console.WriteLine($"credits: {billing.GetProperty("credits").GetInt32()} (ledger ref, not a receipt: " +
$"{(billing.TryGetProperty("receiptRunId", out var r) ? r.GetString() : "-")})");
break;
case 400: // e.g. requester_invalid: requesterVatId is not a valid EU VAT number — nothing ran, nothing was charged
Console.WriteLine(root.GetProperty("error").GetProperty("message").GetString());
break;
case 401: // missing or invalid secret — a person creates one at authorization.createAt
Console.WriteLine(root.GetProperty("authorization").GetProperty("createAt").GetString());
break;
case 402: // not enough credits — a person tops up at error.topUpUrl
Console.WriteLine(root.GetProperty("error").GetProperty("topUpUrl").GetString());
break;
case 403: // per-call limit on this connection — send fewer rows or raise the limit
case 429: // rate limit — wait Retry-After seconds
case 503: // the connection store does not answer — wait Retry-After seconds
default:
Console.WriteLine($"{(int)response.StatusCode} {root}");
break;
}Credits are prepaid on the account (1 credit = 1 euro cent). Without enough, the call answers 402 with the top-up link and VIES is not asked. Every status code and field: openapi.json.
Same key, two more checks
Each answers exactly what the MCP tool of the same name answers. The request body is the tool's arguments.
POST /api/v1/peppol/participant· Free · MCP toollookup_peppol_participantCan this customer receive a Peppol e-invoice today, and if the send still fails, whose problem is it?
POST /api/v1/iban/verify· Free · MCP toolverify_ibanCheck an IBAN's structure and check digits (ISO 13616, ISO 7064), offline
POST /api/v1/payment/preflight· Free · MCP toolpreflight_paymentBefore a payment: compare what the person approved with what is about to be paid, and run the rail's checks
Building an AI agent instead?
Use MCP: https://jithox.com/api/mcp. The tool check_vat_list runs the same check at the same price. Connect your AI
What it is not
- Not a Peppol Access Point. It sends no invoices.
- No tax advice: it reports what VIES says.
- No signed receipt; the answer carries a ledger reference only.