Checks a VAT-ID using the eVatR REST-API of the German Federal Central Tax Office (Bundeszentralamt für Steuern, BZSt)

0.1.0 2025-08-13 16:15 UTC

This package is auto-updated.

Last update: 2025-08-13 16:18:46 UTC


README

Recht logisch eVatR banner image

Latest Version on Packagist Tests codecov Total Downloads

evatr

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).

Installation

You can install the package via composer:

composer require rechtlogisch/evatr

Quick Start

Simple Validation

Validates 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');

Qualified Confirmation

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',
);

Including Raw Response

$result = (new Evatr(
  vatIdOwn: 'DE123456789',
  vatIdForeign: 'ATU12345678'
))->includeRaw()->check();

or with helper functions

$result = checkVatId(
  vatIdOwn: 'DE123456789',
  vatIdForeign: 'ATU12345678',
  includeRaw: true
);

API Reference

Constructor

$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);

Methods

check(): ResultDto

Performs the VAT-ID confirmation:

$result = $evatr->check();

includeRaw(bool $value = true): self

Includes the raw API response in the result:

$evatr->includeRaw(true)->check();

Response Object (ResultDto)

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 array

Status Codes

The 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();

Validation Results (Qualified Confirmation)

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
}

Helper Functions

The package provides convenient helper functions:

checkVatId()

function checkVatId(
    string $vatIdOwn,
    string $vatIdForeign,
    bool $includeRaw = false
): ResultDto;

confirmVatId()

function confirmVatId(
  string $vatIdOwn,
  string $vatIdForeign,
  ?string $company,
  ?string $street,
  ?string $zip,
  ?string $location,
  bool $includeRaw = false
): ResultDto;

Field Mapping

The API uses German terms, which have been mapped to parameters:

Request

BZSt API evatr
anfragendeUstid vatIdOwn
angefragteUstid vatIdForeign
firmenname company
ort location
strasse street
plz zip

Response

BZSt API evatr
id id
anfrageZeitpunkt timestamp
gueltigAb dateFrom
gueltigBis dateTill
ergFirmenname company
ergStrasse street
ergPlz zip
ergOrt location

Language of status messages (EVATR_LANG)

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=en

Warning

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']

Additional endpoints and helpers

The client exposes supplementary endpoints of the eVatR API.

Status messages

$messages = Evatr::getStatusMessages(); // array of DTO\StatusMessage

Each 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.'
);

EU member states availability

$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, ... ]

Error Handling

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()
}

ErrorResponse exception

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
}

Testing

Running Tests

composer test

Test Data

The 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',
];

Environment Variables

For testing with real VAT-IDs, you can set environment variables:

# .env
VATID_OWN=DE123456789
VATID_FOREIGN=ATU12345678

Rate Limits and Best Practices

  • 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

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

If you discover any security-related issues, please email open-source@rechtlogisch.de instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.