Skip to main content

llms.txt

# VatchexVies v1.0.0

Client for the EU VIES REST API (VAT number validation and company information lookup).

Not affiliated with the European Commission or the VIES service.

## Installation

Add to mix.exs:
```elixir
{:vatchex_vies, "~> 1.0"}
```

Runtime dependencies: req, jason.

## Public API

### VatchexVies.lookup/3

```elixir
VatchexVies.lookup(country_code, tin, opts \\ [])
```

Validates a VAT number against the EU VIES service.

**Parameters:**
- `country_code` — ISO 3166-1 alpha-2 country code (e.g., "EL", "DE", "FR")
- `tin` — VAT number / tax identification number
- `opts` — keyword list:
  - `:cache` — module implementing `VatchexVies.Cache` protocol (e.g., `VatchexVies.CachexCache`)
  - `:test_adapter` — `{Req.Test, module}` tuple for test stubbing

**Success return:**
```elixir
{:ok,
 %{
   country_code: "EL",
   afm: "998144460",
   onomasia: "Company Name",
   commer_title: "Trading Name" | nil,
   address: "Street Address\nPostCode City",
   address_collapsed: "Street Address PostCode City",
   source: :vies
 }}
```

- `address` — raw address from VIES (may contain newlines)
- `address_collapsed` — single-line version with newlines collapsed to spaces
- `commer_title` — nil if VIES did not return a trading name

**Error return:**
```elixir
{:error, %{code: atom, descr: String.t()}}
```

**Error codes:**

| code | descr | trigger |
|---|---|---|
| `:invalid_vat` | "Invalid VAT number" | VIES returned valid: false |
| `:invalid_vat` | "VAT number is blank" | Empty/whitespace-only input (no API call) |
| `:vies_http_error` | "HTTP 429" | Non-2xx HTTP status |
| `:vies_too_many_requests` | "Rate limited by VIES" | HTTP 429 specifically |
| `:vies_request_failed` | (dynamic) | Transport/network error |
| `:vies_status_unavailable` | "VIES status endpoint unavailable" | Status check endpoint unreachable |

### VatchexVies.available_countries/1

```elixir
VatchexVies.available_countries(opts \\ [])
```

Returns a map of country codes to VIES availability booleans.
```elixir
{:ok, %{"EL" => true, "FR" => false, ...}}
```

### VatchexVies.available?/2

```elixir
VatchexVies.available?(country_code, opts \\ [])
```

Returns `{:ok, true}` or `{:ok, false}` for a single country.

### VatchexVies.Cache protocol

Pluggable cache backend. Implement for your cache of choice:

```elixir
def get(cache, key)     :: {:ok, value} | :miss
def put(cache, key, value, opts)
```

Opts may include:
- `:ttl` — time-to-live in milliseconds

### VatchexVies.CachexCache

Built-in Cachex v4.x adapter. Conditionally compiled — only available when Cachex is in your dependencies.

Configuration:
```elixir
config :vatchex_vies, :cache_name, :vatchex_vies   # default
config :vatchex_vies, :cache_ttl, 86_400_000        # 24 hours
```

Usage:
```elixir
VatchexVies.lookup("EL", "998144460", cache: VatchexVies.CachexCache)
```

## Caching behavior

- Only successful results (`{:ok, data}`) are cached. Errors never cache.
- Cache key: `"vies:#{country_code}:#{tin}"`
- Default TTL: 24 hours (configurable)
- Zero impact when Cachex is not in your dependency list.

## Testing

```
mix test
```

21 unit tests with Req.Test stubs — no live service calls.