Checks a VAT-ID using the eVatR REST-API of the German Federal Central Tax Office (Bundeszentralamt für Steuern, BZSt)
Caution
This package is in early development and is not yet ready for production use. It is currently being tested and may undergo significant changes.
Important
This is an unofficial wrapper for the eVatR API. For official documentation and terms of use, please refer to the German Federal Central Tax Office (BZSt) website.
Note
This package uses the new REST-API released in July 2025. The old XML-RPC API is being discontinued and will be sunset on November 30th, 2025 (based on information from BZSt-Newsletter USTKV 01/2025 dated July 1st, 2025).
You can install the package via composer:
composer require rechtlogisch/evatrValidates only the VAT-ID without company data verification:
use Rechtlogisch\Evatr\Evatr;
$result = (new Evatr(
vatIdOwn: 'DE123456789', // Your German VAT-ID (required)
vatIdForeign: 'ATU12345678', // VAT-ID to validate (required)
))->check();or alternatively use the helper function:
$result = checkVatId(vatIdOwn: 'DE123456789', vatIdForeign: 'ATU12345678');Validates VAT-ID and verifies company data:
use Rechtlogisch\Evatr\Evatr;
$result = (new Evatr(
vatIdOwn: 'DE123456789', // Your German VAT-ID (required)
vatIdForeign: 'ATU12345678', // VAT-ID to validate (required)
company: 'Musterhaus GmbH & Co KG', // Company name (required for qualified confirmation)
location: 'Musterort', // City (required for qualified confirmation)
street: 'Musterstrasse 22', // Street address (optional)
zip: '12345', // Postal code (optional)
))->check();or alternatively use the helper function:
$result = confirmVatId(
vatIdOwn: 'DE123456789',
vatIdForeign: 'ATU12345678',
company: 'Musterhaus GmbH & Co KG',
street: 'Musterstrasse 22',
zip: '12345',
location: 'Musterort',
);$result = (new Evatr(
vatIdOwn: 'DE123456789',
vatIdForeign: 'ATU12345678'
))->includeRaw()->check();or with helper functions
$result = checkVatId(
vatIdOwn: 'DE123456789',
vatIdForeign: 'ATU12345678',
includeRaw: true
);$evatr = new Evatr(
vatIdOwn: string, // Your German VAT-ID (required)
vatIdForeign: string, // VAT-ID to confirm (required)
company: ?string, // Company name (optional, required for qualified confirmation)
location: ?string, // City (optional, required for qualified confirmation)
street: ?string, // Street address (optional)
zip: ?string, // Postal code (optional)
);or alternatively using RequestDto:
$request = new RequestDto(
vatIdOwn: 'DE123456789',
vatIdForeign: 'ATU12345678',
// ... other parameters
);
$evatr = new Evatr($request);Performs the VAT-ID confirmation:
$result = $evatr->check();Includes the raw API response in the result:
$evatr->includeRaw(true)->check();The check() method returns a ResultDto object with the following methods:
$result->getVatIdOwn(): string; // Own VAT-ID which was used for the request
$result->getVatIdForeign(): string; // Foreign VAT-ID which was checked
$result->getId(): string; // Unique ID from API, related to request
$result->getHttpStatusCode(): ?int; // HTTP status code
$result->getTimestamp(): ?string; // Query timestamp (ISO-8601 string)
$result->getStatus(): ?Status; // Status enum
$result->getMessage(): ?string; // Human-readable message based on EVATR_LANG
$result->getDateFrom(): ?string; // Valid from date
$result->getDateTill(): ?string; // Valid until date
$result->getCompany(): ?QualifiedResult; // Company validation result
$result->getStreet(): ?QualifiedResult; // Street validation result
$result->getZip(): ?QualifiedResult; // ZIP validation result
$result->getLocation(): ?QualifiedResult; // Location validation result
$result->getRaw(): ?string; // Raw API response (if requested)
$result->toArray(): array; // Convert to arrayThe API returns various status codes via the Status enum. All status codes are available as enum cases:
use Rechtlogisch\Evatr\Enum\Status;
// Check the status
if ($result->getStatus() === Status::EVATR_0000) {
// VAT-ID is valid
}
// Get human-readable description
$description = $result->getStatus()->description();For qualified confirmations, the response includes validation results for each field via the QualifiedResult enum:
- A - Data matches registered information
- B - Data does not match registered information
- C - Data was not requested
- D - Data not provided by the EU member state
use Rechtlogisch\Evatr\Enum\QualifiedResult;
if ($result->getCompany() === QualifiedResult::A) {
// Company name matches
}The package provides convenient helper functions:
function checkVatId(
string $vatIdOwn,
string $vatIdForeign,
bool $includeRaw = false
): ResultDto;function confirmVatId(
string $vatIdOwn,
string $vatIdForeign,
?string $company,
?string $street,
?string $zip,
?string $location,
bool $includeRaw = false
): ResultDto;The API uses German terms, which have been mapped to parameters:
| BZSt API | evatr |
|---|---|
| anfragendeUstid | vatIdOwn |
| angefragteUstid | vatIdForeign |
| firmenname | company |
| ort | location |
| strasse | street |
| plz | zip |
| BZSt API | evatr |
|---|---|
| id | id |
| anfrageZeitpunkt | timestamp |
| gueltigAb | dateFrom |
| gueltigBis | dateTill |
| ergFirmenname | company |
| ergStrasse | street |
| ergPlz | zip |
| ergOrt | location |
By default, status messages (human-readable descriptions of evatr-* codes) are returned in German. To switch to English messages, set the following environment variable:
# .env
EVATR_LANG=enWarning
This English translation of the status messages is unofficial. Use at your own risk.
Supported values:
- de (default): German messages
- en: English messages
This affects:
- Status::description()
- ResultDto->toArray()['message']
The client exposes supplementary endpoints of the eVatR API.
$messages = Evatr::getStatusMessages(); // array of DTO\StatusMessageEach StatusMessage item has the shape:
use Rechtlogisch\Evatr\DTO\StatusMessage;
$statusMessage = new StatusMessage(
status: 'evatr-0000',
category: 'Result', // category is always English and language-invariant: Result | Error | Hint
http: 200,
field: null,
message: 'Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt gültig.'
);$states = Evatr::getAvailability(); // array<string,bool> map of code => available
// Example: [ 'DE' => true, 'AT' => false, ... ]
// Only not available:
$notAvailable = Evatr::getAvailability(onlyNotAvailable: true); // [ 'AT' => false, ... ]All public API methods throw exceptions on failure, and return only DTOs on success.
- ErrorResponse: thrown for transport, server, or JSON/response parsing errors.
- InputError: thrown when the error is caused by invalid input, and can be potentially fixed by the user.
use Rechtlogisch\Evatr\Exception\ErrorResponse;
use Rechtlogisch\Evatr\Exception\InputError;
use Rechtlogisch\Evatr\Enum\Status;
try {
$result = checkVatId('DE123456789', 'ATU12345678');
// handle result
} catch (InputError|ErrorResponse $e) {
// Log/handle error: $e->getMessage()
// Get original exception: $e->getException()
}Thrown when a request fails due to transport or response parsing issues.
Fields:
- httpCode: int HTTP status or 0 for client-side failures
- error: string short description
- exception: Throwable the underlying/previous exception; if no underlying Throwable existed, a RuntimeException is used
- raw: ?string raw response body if available
- meta: array<string,mixed> additional context (e.g., endpoint, errorType)
Example:
use Rechtlogisch\Evatr\Exception\ErrorResponse;
try {
$result = checkVatId('DE123456789', 'ATU12345678');
} catch (ErrorResponse $e) {
$code = $e->getHttpCode();
$msg = $e->getError();
$previous = $e->getException(); // underlying Throwable (e.g., JsonException, GuzzleException, or RuntimeException)
$raw = $e->getRaw(); // may be null
$meta = $e->getMeta(); // ['endpoint' => ..., 'errorType' => ...]
// Handle/log accordingly
}composer testThe library includes test VAT-IDs that can be used for development and testing:
// Simple validation test
$testRequest = [
'vatIdOwn' => 'DE123456789',
'vatIdForeign' => 'ATU12345678',
];
// Qualified confirmation test
$qualifiedTestRequest = [
'vatIdOwn' => 'DE123456789',
'vatIdForeign' => 'ATU12345678',
'company' => 'Musterhaus GmbH & Co KG',
'street' => 'Musterstrasse 22',
'zip' => '12345',
'location' => 'Musterort',
];For testing with real VAT-IDs, you can set environment variables:
# .env
VATID_OWN=DE123456789
VATID_FOREIGN=ATU12345678- The API has rate limits. Implement appropriate delays between requests
- Cache results when possible to reduce API calls
- Handle all possible status codes in your application
- Always validate VAT-ID formats before making API calls
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security-related issues, please email [email protected] instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.
