openapi: 3.1.0
info:
  title: Avatcado API
  version: "1.0"
  description: Validate VAT and GST numbers across 32 countries with a single API
    call. Get company details, consultation numbers, and validity status from
    VIES, HMRC, BFS, BRREG, and ABR, with caching, rate limiting, and structured
    error responses built in. Also checks EORI numbers for the EU, Northern
    Ireland and Great Britain.
  contact:
    name: Avatcado
    url: https://docs.avatcado.com
servers:
  - url: https://api.avatcado.com
    description: Production
components:
  schemas:
    VatValidationSuccessResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/VatValidationData"
        meta:
          $ref: "#/components/schemas/ValidationResponseMeta"
      required:
        - data
        - meta
    VatValidationData:
      type: object
      properties:
        valid:
          type: boolean
          description: Whether the VAT number is valid according to the upstream authority
          example: true
        vat_number:
          type: string
          description: The normalized VAT number that was validated
          example: NL123456789B01
        country_code:
          type: string
          description: Two-letter country code extracted from the VAT number
          example: NL
        company:
          $ref: "#/components/schemas/CompanyInfo"
        consultation_number:
          type: string
          description: Consultation number from VIES (EU) or HMRC (UK). Only present when
            requester_vat_number is provided. Not available for CH, LI, NO, or
            AU validations.
          example: WAPIAAAAA1BBB2
        requested_at:
          type: string
          description: When the validation was performed (or originally cached)
          example: 2026-03-06T12:00:00Z
      required:
        - valid
        - vat_number
        - country_code
        - company
        - requested_at
    CompanyInfo:
      type: object
      nullable: true
      properties:
        name:
          type: string
          example: Acme B.V.
          description: Registered company name
        address:
          type: string
          nullable: true
          example: Keizersgracht 123, Amsterdam
          description: Registered company address
      required:
        - name
        - address
      description: Company details returned by the upstream authority. Null if
        invalid, or if the registry publishes no company data (VIES returns none
        for DE and ES).
    ValidationResponseMeta:
      type: object
      properties:
        request_id:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for this request
        request_duration_ms:
          type: integer
          description: Total request processing time in milliseconds
          example: 120
        mode:
          type: string
          enum:
            - test
          description: Present with value 'test' when using an avat_test_ API key
        source:
          type: string
          description: "Registry that produced the served data: vies, hmrc, bfs, brreg,
            abr, a national registry id (anaf, ares, dgfip, fssr, furs, kbo,
            porezna, prh, rik, vid), or test in test mode"
          example: vies
        source_status:
          type: string
          enum:
            - live
            - cached
            - unavailable
            - degraded
            - fallback
          description: How the served result was obtained. 'live' = fresh upstream lookup.
            'cached' = served from the 25-day cache. 'unavailable' = upstream
            down, most recent cached row served (check stale). 'degraded' =
            upstream answered but the result looked like a silent false
            negative, prior row served. 'fallback' = VIES was down and the
            national registry named in source confirmed the number is VAT
            registered.
          example: live
        cached:
          type: boolean
          description: Whether this result was served from cache. Always present
        stale:
          type: boolean
          description: Whether the served cache row is older than the 25-day TTL (only
            true on upstream failure). Always present
        cached_at:
          type: string
          description: When the served row was originally fetched. Present only when
            cached is true
      required:
        - request_id
        - source
        - source_status
        - cached
        - stale
    ErrorResponse:
      type: object
      properties:
        error:
          $ref: "#/components/schemas/ErrorDetail"
        meta:
          $ref: "#/components/schemas/ErrorResponseMeta"
      required:
        - error
        - meta
    ErrorDetail:
      type: object
      properties:
        code:
          type: string
          description: Machine-readable error code
          example: missing_parameter
        message:
          type: string
          description: Human-readable error message
          example: Query parameter 'vat_number' is required
        docs_url:
          type: string
          description: Link to documentation for this error code
          example: https://docs.avatcado.com/errors/missing_parameter
        vat_number:
          type: string
          description: The normalized VAT number from the request, echoed back when one
            was submitted
          example: SE556677889901
        requester_vat_number:
          type: string
          description: The normalized requester VAT number from the request, echoed back
            when supplied
          example: NL861234567B01
        eori_number:
          type: string
          description: The normalized EORI number from the request, echoed back on errors
            from the EORI endpoint
          example: GB123456789000
        details:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
            required:
              - field
              - message
          description: Field-level issues for validation_error responses
      required:
        - code
        - message
    ErrorResponseMeta:
      type: object
      properties:
        request_id:
          type: string
          description: Unique identifier for this request
          example: 550e8400-e29b-41d4-a716-446655440000
        validation_id:
          type: string
          description: Identifier of the recorded failed validation attempt. Present on
            upstream_unavailable and upstream_member_state_unavailable errors.
          example: 7c9e6679-7425-40de-944b-e07fc1f90ae7
      required:
        - request_id
    BatchValidationResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            results:
              type: array
              items:
                anyOf:
                  - $ref: "#/components/schemas/BatchResultSuccess"
                  - $ref: "#/components/schemas/BatchResultError"
            summary:
              $ref: "#/components/schemas/BatchSummary"
          required:
            - results
            - summary
        meta:
          $ref: "#/components/schemas/BatchResponseMeta"
      required:
        - data
        - meta
    BatchResultSuccess:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/VatValidationData"
        meta:
          $ref: "#/components/schemas/BatchItemCacheMeta"
      required:
        - data
        - meta
    BatchItemCacheMeta:
      type: object
      properties:
        source:
          type: string
          description: "Registry that produced the served data: vies, hmrc, bfs, brreg,
            abr, a national registry id (anaf, ares, dgfip, fssr, furs, kbo,
            porezna, prh, rik, vid), or test in test mode"
          example: vies
        source_status:
          type: string
          enum:
            - live
            - cached
            - unavailable
            - degraded
            - fallback
          description: How the served result was obtained. 'live' = fresh upstream lookup.
            'cached' = served from the 25-day cache. 'unavailable' = upstream
            down, most recent cached row served (check stale). 'degraded' =
            upstream answered but the result looked like a silent false
            negative, prior row served. 'fallback' = VIES was down and the
            national registry named in source confirmed the number is VAT
            registered.
          example: live
        cached:
          type: boolean
          description: Whether this result was served from cache. Always present
        stale:
          type: boolean
          description: Whether the served cache row is older than the 25-day TTL (only
            true on upstream failure). Always present
        cached_at:
          type: string
          description: When the served row was originally fetched. Present only when
            cached is true
      required:
        - source
        - source_status
        - cached
        - stale
    BatchResultError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: upstream_unavailable
            message:
              type: string
              example: HMRC service unavailable
            vat_number:
              type: string
              example: GB123456789
              description: The normalized VAT number of the failed item
          required:
            - code
            - message
        meta:
          $ref: "#/components/schemas/BatchItemErrorMeta"
      required:
        - error
        - meta
    BatchItemErrorMeta:
      type: object
      properties:
        vat_number:
          type: string
          example: XX000000000
          description: "Deprecated: use error.vat_number instead. The VAT number that
            failed validation."
          deprecated: true
      required:
        - vat_number
    BatchSummary:
      type: object
      properties:
        total:
          type: integer
          example: 3
          description: Total number of VAT numbers in the request
        succeeded:
          type: integer
          example: 2
          description: Number of successfully validated VAT numbers
        failed:
          type: integer
          example: 1
          description: Number of VAT numbers that failed validation
      required:
        - total
        - succeeded
        - failed
    BatchResponseMeta:
      type: object
      properties:
        request_id:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for this request
        mode:
          type: string
          enum:
            - test
          description: Present with value 'test' when using an avat_test_ API key
        request_duration_ms:
          type: integer
          description: Total request processing time in milliseconds
          example: 120
      required:
        - request_id
    BatchValidateBody:
      type: object
      properties:
        vat_numbers:
          type: array
          items:
            type: string
            minLength: 1
          minItems: 1
          maxItems: 50
          description: Array of VAT numbers to validate (max 50)
          example:
            - NL123456789B01
            - DE987654321
            - GB123456789
            - CHE123456789MWST
            - NO123456789MVA
            - AU51824753556
        requester_vat_number:
          type: string
          description: Your own VAT number, to receive consultation numbers. For EU
            targets, provide an EU requester. For UK targets, provide a UK
            requester.
          example: DE987654321
        cache:
          type: boolean
          description: Set to false to bypass the cache and force fresh lookups. Defaults
            to true, or to false when requester_vat_number is supplied (no
            stored result is served then, not even during an outage). Pass true
            explicitly to opt back in.
          example: true
        fallback:
          type: boolean
          description: Set to false so no item consults a national register when VIES is
            down. Defaults to true, or to false when requester_vat_number is
            supplied, because register answers never carry consultation numbers.
            Pass true explicitly to opt back in.
          example: true
      required:
        - vat_numbers
    AllRatesResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/VatRateData"
        meta:
          allOf:
            - $ref: "#/components/schemas/RatesResponseMeta"
            - type: object
              properties:
                count:
                  type: integer
                  example: 32
                  description: Total number of countries returned
              required:
                - count
      required:
        - data
        - meta
    VatRateData:
      type: object
      properties:
        country_code:
          type: string
          example: NL
          description: ISO 3166-1 alpha-2 country code
        country_name:
          type: string
          example: Netherlands
          description: Full country name
        currency:
          type: string
          example: EUR
          description: ISO 4217 currency code
        standard_rate:
          type: number
          example: 21
          description: Standard VAT rate percentage
        other_rates:
          type: array
          items:
            $ref: "#/components/schemas/VatRateEntry"
          description: Reduced, super-reduced, and zero rates
        updated_at:
          type: string
          example: 2026-01-01T00:00:00.000Z
          description: When this rate data was last updated
      required:
        - country_code
        - country_name
        - currency
        - standard_rate
        - other_rates
        - updated_at
    VatRateEntry:
      type: object
      properties:
        rate:
          type: number
          example: 9
          description: Tax rate percentage
        type:
          type: string
          example: reduced
          description: Rate type (e.g. reduced, super_reduced, zero)
      required:
        - rate
        - type
    RatesResponseMeta:
      type: object
      properties:
        request_id:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for this request
      required:
        - request_id
    SingleRateResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/VatRateData"
        meta:
          $ref: "#/components/schemas/RatesResponseMeta"
      required:
        - data
        - meta
    AsyncSingleResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            request_id:
              type: string
              description: Unique identifier for this async request
              example: 550e8400-e29b-41d4-a716-446655440000
            status:
              type: string
              enum:
                - pending
              example: pending
            vat_number:
              type: string
              description: The normalized VAT number
              example: DE123456789
          required:
            - request_id
            - status
            - vat_number
        meta:
          $ref: "#/components/schemas/ResponseMeta"
      required:
        - data
        - meta
    ResponseMeta:
      type: object
      properties:
        request_id:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for this request
        request_duration_ms:
          type: integer
          description: Total request processing time in milliseconds
          example: 120
        mode:
          type: string
          enum:
            - test
          description: Present with value 'test' when using an avat_test_ API key
        source:
          type: string
          description: "Registry that produced the served data: vies, hmrc, bfs, brreg,
            abr, a national registry id (anaf, ares, dgfip, fssr, furs, kbo,
            porezna, prh, rik, vid), or test in test mode"
          example: vies
        source_status:
          type: string
          enum:
            - live
            - cached
            - unavailable
            - degraded
            - fallback
          description: How the served result was obtained. 'live' = fresh upstream lookup.
            'cached' = served from the 25-day cache. 'unavailable' = upstream
            down, most recent cached row served (check stale). 'degraded' =
            upstream answered but the result looked like a silent false
            negative, prior row served. 'fallback' = VIES was down and the
            national registry named in source confirmed the number is VAT
            registered.
          example: live
        cached:
          type: boolean
          description: Whether this result was served from cache. Always present
        stale:
          type: boolean
          description: Whether the served cache row is older than the 25-day TTL (only
            true on upstream failure). Always present
        cached_at:
          type: string
          description: When the served row was originally fetched. Present only when
            cached is true
      required:
        - request_id
    AsyncSingleRequest:
      type: object
      properties:
        vat_number:
          type: string
          minLength: 1
          description: The VAT number to validate asynchronously
          example: DE123456789
        requester_vat_number:
          type: string
          description: Your own VAT number (for consultation numbers)
          example: NL987654321B01
        cache:
          type: boolean
          description: Whether to use cached results. Defaults to true, or to false when
            requester_vat_number is supplied (no stored result is served then,
            not even during an outage). Pass true explicitly to opt back in.
          example: true
        fallback:
          type: boolean
          description: Whether a VIES outage may be answered from the national register
            (Pro and Business). Defaults to true, or to false when
            requester_vat_number is supplied, because register answers never
            carry a consultation number. Pass true explicitly to opt back in.
          example: true
      required:
        - vat_number
    AsyncBatchResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            batch_id:
              type: string
              nullable: true
              description: Unique identifier for this batch (null when every item was
                rejected)
              example: 550e8400-e29b-41d4-a716-446655440000
            status:
              type: string
              enum:
                - pending
                - completed
              description: "`pending` for queued batches, `completed` for all-rejected batches
                that were never queued"
              example: pending
            total:
              type: integer
              description: Total number of items submitted
              example: 200
            accepted:
              type: integer
              description: Number of items accepted for processing
              example: 197
            rejected:
              type: array
              items:
                type: object
                properties:
                  vat_number:
                    type: string
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                required:
                  - vat_number
                  - error
              description: Items rejected due to invalid format
          required:
            - batch_id
            - status
            - total
            - accepted
            - rejected
        meta:
          $ref: "#/components/schemas/ResponseMeta"
      required:
        - data
        - meta
    AsyncBatchRequest:
      type: object
      properties:
        vat_numbers:
          type: array
          items:
            type: string
            minLength: 1
          minItems: 1
          description: Array of VAT numbers to validate (max 200 for Pro, 1000 for Business)
          example:
            - DE123456789
            - NL987654321B01
        requester_vat_number:
          type: string
          description: Your own VAT number (for consultation numbers)
          example: NL987654321B01
        cache:
          type: boolean
          description: Whether to use cached results. Defaults to true, or to false when
            requester_vat_number is supplied (no stored result is served then,
            not even during an outage). Pass true explicitly to opt back in.
          example: true
        fallback:
          type: boolean
          description: Whether a VIES outage may be answered from the national register
            (Pro and Business). Defaults to true, or to false when
            requester_vat_number is supplied, because register answers never
            carry consultation numbers. Pass true explicitly to opt back in.
          example: true
      required:
        - vat_numbers
    EoriValidationSuccessResponse:
      type: object
      properties:
        data:
          $ref: "#/components/schemas/EoriValidationData"
        meta:
          $ref: "#/components/schemas/EoriValidationMeta"
      required:
        - data
        - meta
    EoriValidationData:
      type: object
      properties:
        valid:
          type: boolean
          description: Whether the register lists the EORI number as valid
          example: true
        eori_number:
          type: string
          description: The normalized EORI number that was checked
          example: GB123456789000
        country_code:
          type: string
          description: Two-letter prefix of the EORI number (XI for Northern Ireland)
          example: GB
        company:
          allOf:
            - $ref: "#/components/schemas/CompanyInfo"
            - description: Trader name and address from the register. Null when the number is
                not valid, and null for a valid number whose trader did not
                agree to publication, which is common.
        requested_at:
          type: string
          description: "When the register was asked (for a cached answer: when it was
            first stored)"
          example: 2026-11-13T09:19:29.000Z
      required:
        - valid
        - eori_number
        - country_code
        - company
        - requested_at
    EoriValidationMeta:
      type: object
      properties:
        request_id:
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
          description: Unique identifier for this request
        request_duration_ms:
          type: integer
          description: Total request processing time in milliseconds
          example: 120
        mode:
          type: string
          enum:
            - test
          description: Present with value 'test' when using an avat_test_ API key
        source:
          type: string
          enum:
            - ec_eos
            - hmrc
            - test
          description: "Register that produced the answer: ec_eos (the European
            Commission's EORI validation service, for EU and XI numbers), hmrc
            (for GB numbers), or test in test mode"
          example: hmrc
        source_status:
          type: string
          enum:
            - live
            - cached
            - unavailable
          description: How the answer was obtained. 'live' = fresh check. 'cached' =
            served from the cache. 'unavailable' = the register was down and the
            most recent stored answer was served (check stale).
          example: live
        cached:
          type: boolean
          description: Whether this result was served from cache. Always present
        stale:
          type: boolean
          description: Whether the served stored answer is older than the cache window
            (only true when the register is down). Always present
        cached_at:
          type: string
          description: When the served row was originally fetched. Present only when
            cached is true
      required:
        - request_id
        - source
        - source_status
        - cached
        - stale
  parameters: {}
paths:
  /v1/validate:
    get:
      operationId: validateVatNumber
      summary: Validate a VAT number
      description: >-
        Validate a VAT number against the appropriate upstream service (VIES,
        HMRC, BFS, BRREG, or ABR) and return the validity status, company
        details, and an optional consultation number.


        Input is normalized automatically - spaces, dots, and casing are handled
        for you (`"nl 123.456.789 b01"` -> `"NL123456789B01"`).


        Results are cached for 25 days. Every response carries meta.source
        (which registry answered), meta.source_status (live, cached,
        unavailable, degraded or fallback), meta.cached and meta.stale. On Pro
        and Business plans, when VIES is down for one of the 10 EU countries
        with an open national tax register (BE, CZ, EE, FI, FR, HR, LV, RO, SI,
        SK) that register is consulted and served when it confirms the number
        (source_status "fallback"); otherwise, and on the Free plan, the most
        recent stored result is served (source_status "unavailable"). Pass
        cache=false to bypass the cache and force a fresh lookup, and
        fallback=false so this request does not consult a national register.
        Both default to true, except when requester_vat_number is supplied: a
        consultation-number request then skips the cache, the register and any
        stored result, and answers with a fresh check or a 503. Pass cache=true
        or fallback=true explicitly to opt back in.


        Company data is null for DE and ES: neither member state publishes
        company names or addresses through VIES.


        An EORI number is a separate registration from a VAT number. Use GET
        /v1/eori/validate for EORI numbers.
      tags:
        - VAT Validation
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            minLength: 1
            description: The VAT number to validate (e.g. NL123456789B01, GB123456789,
              CHE-123.456.789 MWST, NO123456789MVA, AU51824753556). Supports EU,
              UK, CH, LI, NO, and AU formats. Will be normalized to uppercase
              with spaces and dots removed.
            example: NL123456789B01
          required: true
          description: The VAT number to validate (e.g. NL123456789B01, GB123456789,
            CHE-123.456.789 MWST, NO123456789MVA, AU51824753556). Supports EU,
            UK, CH, LI, NO, and AU formats. Will be normalized to uppercase with
            spaces and dots removed.
          name: vat_number
          in: query
        - schema:
            type: string
            description: Your own VAT number, to receive a consultation number. For EU
              targets, provide an EU requester. For UK targets, provide a UK
              requester.
            example: DE987654321
          required: false
          description: Your own VAT number, to receive a consultation number. For EU
            targets, provide an EU requester. For UK targets, provide a UK
            requester.
          name: requester_vat_number
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Set to false to bypass the cache and force a fresh lookup from the
              upstream service. The fresh result is still cached for subsequent
              requests. Defaults to true, or to false when requester_vat_number
              is supplied, so that a consultation number always comes from a
              fresh check. With a requester and the cache off, no stored result
              is served during an outage either. Pass true explicitly to opt
              back in.
            example: "true"
          required: false
          description: Set to false to bypass the cache and force a fresh lookup from the
            upstream service. The fresh result is still cached for subsequent
            requests. Defaults to true, or to false when requester_vat_number is
            supplied, so that a consultation number always comes from a fresh
            check. With a requester and the cache off, no stored result is
            served during an outage either. Pass true explicitly to opt back in.
          name: cache
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Set to false so this request does not consult a national register
              when VIES is down. Applies on Pro and Business plans and is
              ignored on Free. Defaults to true, or to false when
              requester_vat_number is supplied, because register answers never
              carry a consultation_number. Pass true explicitly to accept a
              register answer next to a requester.
            example: "true"
          required: false
          description: Set to false so this request does not consult a national register
            when VIES is down. Applies on Pro and Business plans and is ignored
            on Free. Defaults to true, or to false when requester_vat_number is
            supplied, because register answers never carry a
            consultation_number. Pass true explicitly to accept a register
            answer next to a requester.
          name: fallback
          in: query
      responses:
        "200":
          description: VAT number validated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VatValidationSuccessResponse"
        "400":
          description: Missing required query parameter `vat_number`
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: missing_parameter
                  message: "Missing required query parameter: vat_number"
                  docs_url: https://docs.avatcado.com/errors/missing_parameter
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "422":
          description: Invalid VAT number format or unsupported country
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: invalid_vat_format
                  message: "The VAT number format is invalid. Expected format: CC123456789"
                  docs_url: https://docs.avatcado.com/errors/invalid_vat_format
                  vat_number: DE123
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Monthly quota or burst rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly validation limit exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  vat_number: DE123456789
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "503":
          description: Upstream validation service (VIES, HMRC, BFS, BRREG, or ABR) error
            or unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: upstream_unavailable
                  message: The upstream VAT validation service is currently unavailable
                  docs_url: https://docs.avatcado.com/errors/upstream_unavailable
                  vat_number: SE556677889901
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
                  validation_id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
  /v1/validate/batch:
    post:
      operationId: batchValidateVatNumbers
      summary: Validate multiple VAT numbers
      description: >-
        Validate up to 50 VAT numbers in a single request. Each VAT number is
        validated independently against the appropriate upstream service (VIES,
        HMRC, BFS, BRREG, or ABR). Results are returned in the same order as the
        input array.


        Duplicate VAT numbers are deduplicated internally. Only unique numbers
        count against your monthly quota. Per-item errors are returned inline in
        the results array; the overall response is always 200.


        Available to Pro and Business tiers only.
      tags:
        - VAT Validation
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchValidateBody"
      responses:
        "200":
          description: Batch validation completed. Per-item errors are inline in results.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchValidationResponse"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "403":
          description: Batch validation requires Pro or Business tier
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: tier_insufficient
                  message: Batch validation requires a Pro or Business plan
                  docs_url: https://docs.avatcado.com/errors/tier_insufficient
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "422":
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: validation_error
                  message: At least one VAT number is required
                  docs_url: https://docs.avatcado.com/errors/validation_error
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Monthly quota insufficient for this batch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly validation limit exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  vat_number: DE123456789
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
  /v1/rates:
    get:
      operationId: listVatRates
      summary: List VAT rates for all supported countries
      description: >-
        Returns current VAT rates (standard, reduced, super-reduced, zero) for
        all EU member states, the United Kingdom, Switzerland, Liechtenstein,
        and Norway.


        This endpoint is free and unlimited. It does not count against your
        monthly quota. Burst rate limiting still applies to protect
        infrastructure.
      tags:
        - VAT Rates
      security:
        - bearerAuth: []
      responses:
        "200":
          description: List of VAT rates for all supported countries
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AllRatesResponse"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Burst rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly validation limit exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  vat_number: DE123456789
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
  /v1/rates/{country_code}:
    get:
      operationId: getVatRateByCountry
      summary: Get VAT rates for a specific country
      description: >-
        Returns current VAT rates for a single country identified by its ISO
        3166-1 alpha-2 code (e.g. NL, DE, GB, CH, NO).


        This endpoint is free and unlimited. It does not count against your
        monthly quota.
      tags:
        - VAT Rates
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            minLength: 2
            maxLength: 2
            description: ISO 3166-1 alpha-2 country code (e.g. NL, DE, GB, CH, NO)
            example: NL
          required: true
          description: ISO 3166-1 alpha-2 country code (e.g. NL, DE, GB, CH, NO)
          name: country_code
          in: path
      responses:
        "200":
          description: VAT rates for the requested country
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SingleRateResponse"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "404":
          description: No VAT rates found for the specified country code
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: not_found
                  message: "No VAT rates found for country code: XX"
                  docs_url: https://docs.avatcado.com/errors/not_found
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Burst rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly validation limit exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  vat_number: DE123456789
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
  /v1/validate/async:
    post:
      operationId: asyncValidateVatNumber
      summary: Validate a VAT number asynchronously
      description: >-
        Submit a VAT number for asynchronous validation. Returns 202
        immediately. The result is delivered to your configured webhook URL when
        processing is complete. Invalid VAT formats are rejected immediately
        (422), never queued.


        Pro and Business tiers only. Requires a configured webhook URL. Test
        keys are rejected.
      tags:
        - Async Validation
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AsyncSingleRequest"
      responses:
        "202":
          description: Validation request accepted for processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncSingleResponse"
        "400":
          description: No webhook URL configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: webhook_not_configured
                  message: Configure a webhook URL before using async validation. See
                    https://docs.avatcado.com/webhooks
                  docs_url: https://docs.avatcado.com/errors/webhook_not_configured
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "403":
          description: Async validation requires Pro or Business tier, or the key is a
            test key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: tier_insufficient
                  message: Async validation requires a Pro or Business plan
                  docs_url: https://docs.avatcado.com/errors/tier_insufficient
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "422":
          description: Invalid VAT number format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: invalid_vat_format
                  message: "The VAT number format is invalid. Expected format: CC123456789"
                  docs_url: https://docs.avatcado.com/errors/invalid_vat_format
                  vat_number: DE123
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Monthly quota or burst rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly validation limit exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  vat_number: DE123456789
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
  /v1/validate/async/batch:
    post:
      operationId: asyncBatchValidateVatNumbers
      summary: Validate multiple VAT numbers asynchronously
      description: >-
        Submit an array of VAT numbers for asynchronous validation. Returns 202
        immediately with an accepted/rejected breakdown. Items with invalid
        formats are rejected inline in the response (never queued). Results for
        all accepted items are delivered as a single `batch.completed` webhook
        event.


        Pro tier: max 200 items. Business tier: max 1000 items. Test keys are
        rejected.
      tags:
        - Async Validation
      security:
        - bearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AsyncBatchRequest"
      responses:
        "202":
          description: Batch accepted for processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncBatchResponse"
        "400":
          description: No webhook URL configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: webhook_not_configured
                  message: Configure a webhook URL before using async validation. See
                    https://docs.avatcado.com/webhooks
                  docs_url: https://docs.avatcado.com/errors/webhook_not_configured
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "403":
          description: Async validation requires Pro or Business tier, or the key is a
            test key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: tier_insufficient
                  message: Async validation requires a Pro or Business plan
                  docs_url: https://docs.avatcado.com/errors/tier_insufficient
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "422":
          description: Batch size exceeds tier limit or invalid request body
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: validation_error
                  message: At least one VAT number is required
                  docs_url: https://docs.avatcado.com/errors/validation_error
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Monthly quota insufficient for this batch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly validation limit exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  vat_number: DE123456789
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
  /v1/eori/validate:
    get:
      operationId: validateEoriNumber
      summary: Validate an EORI number
      description: >-
        Check an EORI number (the customs identifier a business needs to import,
        export or move goods in transit) against the register that issued it. GB
        numbers are checked with HMRC; numbers from the 27 EU member states and
        XI (Northern Ireland) are checked with the European Commission's EORI
        validation service.


        Input is normalized automatically: spaces, dots, dashes and casing are
        handled for you (`"gb 123.456.789 000"` -> `"GB123456789000"`). A number
        neither register would accept is rejected with a 422 before any register
        is called, and does not count against your quota.


        company is null for many valid numbers: both registers publish a
        trader's name and address only when the trader agreed to it.


        Answers are cached. Every response carries meta.source (ec_eos or hmrc),
        meta.source_status (live, cached or unavailable), meta.cached and
        meta.stale. When the register is down, the most recent stored answer is
        served with source_status "unavailable"; with no stored answer the
        request ends in a 503 that is not counted. Pass cache=false to force a
        fresh check: the request then answers with a fresh check or a 503.


        An EORI number and a VAT number are separate registrations. A valid VAT
        number does not imply a valid EORI number. Use GET /v1/validate for VAT
        numbers.
      tags:
        - EORI Validation
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            minLength: 1
            description: "The EORI number to check: a two-letter country code followed by 1
              to 15 letters or digits (GB numbers: 12 to 15 digits). Supports
              the 27 EU member states, XI and GB. Will be normalized to
              uppercase with spaces, dots and dashes removed, and the Greek VAT
              prefix EL is rewritten to the EORI prefix GR."
            example: GB123456789000
          required: true
          description: "The EORI number to check: a two-letter country code followed by 1
            to 15 letters or digits (GB numbers: 12 to 15 digits). Supports the
            27 EU member states, XI and GB. Will be normalized to uppercase with
            spaces, dots and dashes removed, and the Greek VAT prefix EL is
            rewritten to the EORI prefix GR."
          name: eori_number
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: "Set to false to skip the cache and check the register again. The
              fresh answer is still stored for later requests. With the cache
              off, no stored answer is served when the register is down: the
              request answers with a fresh check or a 503. Defaults to true."
            example: "true"
          required: false
          description: "Set to false to skip the cache and check the register again. The
            fresh answer is still stored for later requests. With the cache off,
            no stored answer is served when the register is down: the request
            answers with a fresh check or a 503. Defaults to true."
          name: cache
          in: query
      responses:
        "200":
          description: EORI number checked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EoriValidationSuccessResponse"
        "400":
          description: Missing required query parameter `eori_number`
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: missing_parameter
                  message: "Missing required query parameter: eori_number"
                  docs_url: https://docs.avatcado.com/errors/missing_parameter
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: unauthorized
                  message: Missing or invalid API key
                  docs_url: https://docs.avatcado.com/errors/unauthorized
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "422":
          description: Invalid EORI number format or unsupported country
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: invalid_eori_format
                  message: "Invalid EORI number format: GB123. GB EORI numbers are GB followed by
                    12 to 15 digits"
                  docs_url: https://docs.avatcado.com/errors/invalid_eori_format
                  eori_number: GB123
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "429":
          description: Monthly quota or burst rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: rate_limit_exceeded
                  message: Monthly limit of 500 requests exceeded
                  docs_url: https://docs.avatcado.com/errors/rate_limit_exceeded
                  eori_number: GB123456789000
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
        "503":
          description: The register (HMRC or the EU EORI validation service) is
            unavailable and no stored answer exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  code: upstream_unavailable
                  message: The HMRC EORI service is unavailable
                  docs_url: https://docs.avatcado.com/errors/upstream_unavailable
                  eori_number: GB123456789000
                meta:
                  request_id: 550e8400-e29b-41d4-a716-446655440000
