openapi: 3.0.3
info:
  title: GoodVat API
  version: 1.0.0
  description: >
    API for global tax compliance: an organisation's products, customers, seller addresses and tax registrations, and the public reference data behind them.
servers:
  - url: 'https://api.goodvat.com/v1'
    description: Production Server
tags:
  - name: Product
    description: CRUD management of an organisation's sellable products (SKUs), used to drive VAT/sales-tax rate classification
  - name: Customer
    description: CRUD management of an organisation's customers, including their postal address, tax id, and discount
  - name: Seller Address
    description: >
      CRUD management of the locations of the organisation's legal entities (the sellers): their head office (`HEAD_OFFICE`) and other establishments (`OFFICE`), where their goods are stored (`WAREHOUSE`) and where they have people working (`PEOPLE`). A location belongs to the legal entity it names, or to the organisation's default entity. Its establishments decide where the seller is based. Locations are never edited or deleted, only retired, so transactions keep the origin they were calculated with. Each legal entity has at most one active head office and one default supplied-from location.
  - name: Tax Registration
    description: >
      CRUD management of the tax registrations of an organisation's legal entities (e.g. a UK VAT number), each under a registration scheme with one authority covering one or more tax jurisdictions and the sales it covers: `STANDARD` (e.g. `UK VAT`), `SIMPLIFIED` (e.g. `Norway VOEC`) or `ONE_STOP_SHOP` (e.g. `Non-Union OSS (Ireland)`, one per member state of identification). A registration belongs to the legal entity it names, or to the organisation's default entity. Threshold monitoring shows where the organisation's recorded sales mean it must register.
  - name: Reference Data
    description: >
      Public reference data (product categories), searchable for autocomplete pickers. No API key required; when one is sent it is accepted.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API_KEY
  schemas:
    ValidationResult:
      type: object
      description: Standard error envelope returned for validation failures, conflicts, and not-found responses.
      properties:
        errors:
          type: array
          items:
            type: string
          example:
            - "sku is required"

    MoneyV1:
      type: object
      required:
        - currency
        - value
      properties:
        currency:
          type: string
          description: The ISO currency code (lowercase).
          example: 'usd'
        value:
          type: number
          format: double
          description: The monetary amount.
          example: 4.50

    ProductCategoryCode:
      type: string
      description: >
        Product/service category code used to drive VAT and sales-tax rate classification for the product.
      enum:
        - GOODS_GEN
        - GOODS_INSTALLED
        - GOODS_JEWELRY
        - GOODS_BICYCLE
        - SVC_GEN
        - SVC_ACCOMMODATION
        - SVC_PASSENGER_TRANSPORT
        - SVC_PASSENGER_LOCAL
        - SVC_PASSENGER_RAIL
        - SVC_PASSENGER_COACH
        - SVC_PASSENGER_TAXI
        - SVC_PASSENGER_RIDE_HAILING
        - SVC_PASSENGER_AIR_DOMESTIC
        - SVC_PASSENGER_AIR_INTERNATIONAL
        - SVC_PASSENGER_WATER
        - SVC_PASSENGER_WATER_INTERNATIONAL
        - SVC_PASSENGER_CABLE
        - SVC_EVENT_ADMISSION
        - SVC_THEATRE_ADMISSION
        - SVC_CONCERT_ADMISSION
        - SVC_CINEMA_ADMISSION
        - SVC_SPORTING_EVENT_ADMISSION
        - SVC_CONFERENCE_ADMISSION
        - SVC_TRADE_SHOW_ADMISSION
        - SVC_GYM_ACCESS
        - SVC_SWIMMING_POOL_ACCESS
        - SVC_SPORTS_FACILITIES
        - SVC_FITNESS_CLASSES
        - SVC_FITNESS_CLASSES_OFFSITE
        - SW_SAAS
        - SW_DL
        - SW_CUSTOM
        - DIGI_MEDIA_MUSIC
        - DIGI_MEDIA_PODCASTS
        - DIGI_MEDIA_VIDEO
        - DIGI_MEDIA_LIVESTREAM
        - DIGI_MEDIA_BROADCAST
        - DIGI_MEDIA_GAMES
        - DIGI_ASSETS
        - DIGI_EBOOK
        - DIGI_NEWSPAPERS
        - DIGI_NEWSPAPERS_WEEKLY
        - DIGI_PERIODICALS
        - DIGI_AUDIOBOOK
        - EDU_ONLINE
        - EDU_ON_DEMAND
        - EDU_INPERSON
        - PRINT_BOOKS
        - PRINT_NEWSPAPERS
        - PRINT_NEWSPAPERS_WEEKLY
        - PRINT_PERIODICALS
        - FOOD_GROCERY
        - FOOD_COCOA_SPREADS
        - FOOD_BREAD
        - FOOD_MILK
        - FOOD_PLANT_MILK
        - FOOD_CREAM
        - FOOD_YOGHURT
        - FOOD_BUTTER
        - FOOD_CHEESE
        - FOOD_EGGS
        - FOOD_FRESH_MEAT
        - FOOD_FRESH_POULTRY
        - FOOD_FROZEN_MEAT
        - FOOD_FROZEN_POULTRY
        - FOOD_MEAT_PRODUCTS
        - FOOD_FRESH_FISH
        - FOOD_SEAFOOD
        - FOOD_FROZEN_FISH
        - FOOD_FISH_PRODUCTS
        - FOOD_FRESH_VEGETABLES
        - FOOD_FROZEN_FRUIT_VEGETABLES
        - FOOD_FRESH_APPLES_PEARS
        - FOOD_FRESH_STONE_FRUIT
        - FOOD_FRESH_BERRIES
        - FOOD_FRESH_FRUIT
        - FOOD_NUTS_DRIED_FRUIT
        - FOOD_JAM
        - FOOD_HONEY
        - FOOD_RICE
        - FOOD_FLOUR
        - FOOD_PASTA
        - FOOD_CEREAL_GRAINS
        - FOOD_DRIED_LEGUMES
        - FOOD_OLIVE_OIL
        - FOOD_COOKING_OIL
        - FOOD_SUGAR
        - FOOD_SALT
        - FOOD_SPICES
        - FOOD_COFFEE
        - FOOD_TEA
        - FOOD_HERBAL_TEA
        - FOOD_INFANT_FORMULA
        - FOOD_BABY
        - FOOD_CANDY
        - FOOD_CAKES_PASTRIES
        - FOOD_BISCUITS
        - FOOD_BISCUITS_CHOCOLATE
        - FOOD_SOFT_DRINK
        - FOOD_WATER
        - FOOD_JUICE
        - FOOD_PREPARED
        - FOOD_SUPPLEMENT
        - FOOD_SNACKS
        - ALCOHOL_BEVERAGES
        - TOBACCO_PRODUCTS
        - APPRL_GEN
        - APPRL_CHILD
        - APPRL_SAFETY
        - APPRL_HELMET
        - SAFETY_CHILD_CAR_SEAT
        - BABY_NAPPIES
        - HEALTH_RX
        - HEALTH_RX_ORAL
        - HEALTH_OTC
        - HEALTH_OTC_ORAL
        - HEALTH_DEVICE
        - HEALTH_MEDICAL_EQUIPMENT
        - HEALTH_GLUCOSE_TESTING
        - HEALTH_LANCETS
        - HEALTH_INSULIN_NEEDLES
        - HEALTH_INSULIN_PENS
        - HEALTH_INSULIN_PUMPS
        - HEALTH_WOUND_DRESSINGS
        - HEALTH_FIRST_AID_KITS
        - HEALTH_NEBULISERS
        - HEALTH_DISABILITY_AID
        - HEALTH_HYGIENE
        - HEALTH_INCONTINENCE
        - HEALTH_SMOKING_CESSATION
        - HEALTH_CONTRACEPTIVE
        - HEALTH_EYEGLASSES
        - HEALTH_CONTACT_LENSES
        - SVC_CONSULT_B2B
        - SVC_LEGAL
        - SVC_ACCOUNTING
        - SVC_EXTENDED_WARRANTY
        - TELECOM_SVC
        - TELECOM_MOBILE_DATA
        - AGRI_SUPPLIES
        - AGRI_FEED_LIVESTOCK
        - AGRI_SEEDS_FOOD_PLANTS
        - AGRI_FERTILISER
        - GARDEN_LIVE_PLANTS
        - GARDEN_CUT_FLOWERS
        - PET_FOOD
        - EQUINE_SUPPLIES
        - ENERGY_DOMESTIC_FUEL
        - ENERGY_SOLAR_PANELS
        - ENERGY_HOME_BATTERY
        - ELEC_MOBILE_PHONE
        - ELEC_TABLET
        - ELEC_LAPTOP
        - ELEC_GAME_CONSOLE
        - ELEC_COMPUTER_CHIP
        - VEHICLES_GENERAL
        - MFG_MACHINERY
        - VENDING_GOODS
      example: 'GOODS_GEN'

    ProductV1:
      type: object
      description: >
        Something the organisation sells, across its integrations. Archived, never deleted: transaction lines reference it
        and keep the category they were calculated with.
      required: [ id, name, productCategory, state, identities ]
      properties:
        id:
          type: string
          format: uuid
          example: 'b1e6c9a2-8f34-4e2d-9c11-6a2f8d7e4b10'
        name:
          type: string
          example: 'Black Ceramic Mug'
        description:
          type: string
          nullable: true
          example: '350ml matte black ceramic mug'
        productCategory:
          $ref: '#/components/schemas/ProductCategoryCode'
        state:
          type: string
          enum: [ ACTIVE, ARCHIVED ]
        externalId:
          type: string
          nullable: true
          description: The product's ID in your own systems (its identity at the API).
          example: 'mug-blk-001'
        sku:
          type: string
          nullable: true
          description: Its SKU at the API.
          example: 'MUG-BLK-001'
        identities:
          type: array
          description: Who the product is at each connection (a Shopify product, the API).
          items:
            $ref: '#/components/schemas/ProductIdentityV1'

    ProductIdentityV1:
      type: object
      required: [ connectionId, integration, externalId ]
      properties:
        connectionId:
          type: string
          format: uuid
        integration:
          type: string
          description: The integration's slug, as on goodvat.com/integrations (`shopify`, `api`).
          example: 'api'
        externalId:
          type: string
        sku:
          type: string
          nullable: true

    ProductCreationRequestV1:
      type: object
      required: [ name, productCategory ]
      properties:
        name:
          type: string
          example: 'Black Ceramic Mug'
        description:
          type: string
          nullable: true
        productCategory:
          $ref: '#/components/schemas/ProductCategoryCode'
        externalId:
          type: string
          nullable: true
          description: The product's ID in your own systems; unique. The SKU stands in when omitted.
          example: 'mug-blk-001'
        sku:
          type: string
          nullable: true
          example: 'MUG-BLK-001'

    ProductCreationResponseV1:
      type: object
      required:
        - product
      properties:
        product:
          $ref: '#/components/schemas/ProductV1'

    ProductUpdateRequestV1:
      type: object
      description: >
        Partial-update payload. Only `id` is required; a field omitted (or `null`) is left unchanged. Recategorising a
        product never changes recorded transactions.
      required:
        - id
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        productCategory:
          $ref: '#/components/schemas/ProductCategoryCode'
        externalId:
          type: string
          nullable: true
        sku:
          type: string
          nullable: true

    ProductUpdateResponseV1:
      type: object
      required:
        - product
      properties:
        product:
          $ref: '#/components/schemas/ProductV1'

    PageV1:
      type: object
      description: >
        One page of a list, as every paged endpoint returns it; each list's page schema adds its items as `content`. Request a page with the standard `page` (zero-based) and `size` query parameters.
      required: [ totalElements, totalPages, size, number, numberOfElements, first, last, empty ]
      properties:
        totalElements:
          type: integer
          format: int64
          description: The items across every page.
          example: 42
        totalPages:
          type: integer
          format: int32
          example: 3
        size:
          type: integer
          format: int32
          description: The requested page size.
          example: 20
        number:
          type: integer
          format: int32
          description: This page's zero-based index.
          example: 0
        numberOfElements:
          type: integer
          format: int32
          description: The items on this page.
          example: 20
        first:
          type: boolean
          example: true
        last:
          type: boolean
          example: false
        empty:
          type: boolean
          example: false

    ProductPageV1:
      description: A page of products.
      allOf:
        - $ref: '#/components/schemas/PageV1'
        - type: object
          required: [ content ]
          properties:
            content:
              type: array
              items:
                $ref: '#/components/schemas/ProductV1'

    ProductCategoryV1:
      type: object
      description: A product category, the classification that decides a product's VAT and sales-tax rates.
      required:
        - code
        - name
        - type
        - deliveryMethod
      properties:
        code:
          $ref: '#/components/schemas/ProductCategoryCode'
        name:
          type: string
          description: Display name.
          example: 'E-books'
        description:
          type: string
          description: What the category covers, how it is commonly taxed, and which categories cover its neighbours.
          example: 'Electronic books. Zero-rated in the UK since 1 May 2020; reduced rates across much of the EU (5.5% in France).'
        type:
          type: string
          enum: [ GOODS, SERVICES ]
          example: 'GOODS'
        deliveryMethod:
          type: string
          enum: [ PHYSICAL_SHIPPED, IN_PERSON, DIGITAL_DOWNLOAD, DIGITAL_CLOUD, DIGITAL_LIVE ]
          example: 'DIGITAL_DOWNLOAD'

    ProductCategoryPageV1:
      description: A page of product categories, best match first.
      allOf:
        - $ref: '#/components/schemas/PageV1'
        - type: object
          required: [ content ]
          properties:
            content:
              type: array
              items:
                $ref: '#/components/schemas/ProductCategoryV1'

    CustomerTypeCode:
      type: string
      description: Classification of the customer, used for tax-exemption and reporting purposes.
      enum:
        - PERSONAL
        - BUSINESS
        - BUSINESS_AGRICULTURE
        - BUSINESS_EQUINE
        - BUSINESS_MANUFACTURING
        - GOVERNMENT
        - NGO
        - UK_CHARITY
        - DIPLOMAT
        - DISABLED_INDIVIDUAL
        - UNKNOWN
      example: 'BUSINESS'

    DiscountV1:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum: [ PERCENTAGE, FLAT_AMOUNT ]
          example: 'PERCENTAGE'
        value:
          type: number
          format: double
          description: For `PERCENTAGE`, a value out of 100 (e.g. `10` means 10%). For `FLAT_AMOUNT`, a flat monetary amount in the transaction's currency.
          example: 10

    RateV1:
      type: object
      description: Tax rate applied by a tax jurisdiction.
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum: [ PERCENTAGE, FLAT_AMOUNT ]
          example: 'PERCENTAGE'
        value:
          type: number
          format: double
          example: 0.085

    InvoiceShippingV1:
      type: object
      description: Shipping line details for tax calculation and invoicing.
      required:
        - type
        - amount
      properties:
        type:
          type: string
          description: Type/method of shipping (e.g. `SHIPPING_COMMON_CARRIER`, `STANDARD`).
          example: 'SHIPPING_COMMON_CARRIER'
        amount:
          $ref: '#/components/schemas/MoneyV1'

    CustomerAddressV1:
      type: object
      description: >
        A postal address of the organisation's. The same content (case-insensitive, trimmed) is reused by reference
        rather than duplicated, and an address is never edited or deleted.
      required:
        - name
      properties:
        name:
          type: string
          description: A human-readable label for the address (e.g. "Head Office", "Warehouse"). Mandatory.
          example: 'Head Office'
        country:
          type: string
          nullable: true
          description: ISO-3166-1 alpha-3 country code.
          example: 'GBR'
        region:
          type: string
          nullable: true
          example: 'London'
        postalCode:
          type: string
          nullable: true
          example: 'SW1A 1AA'
        city:
          type: string
          nullable: true
          example: 'London'
        line1:
          type: string
          nullable: true
          example: '10 Downing St'

    CustomerV1:
      type: object
      description: >
        A buyer of the organisation's, across its integrations: who it is at each connection, the tax IDs it holds and its
        addresses. Archived, never deleted: transactions reference it.
      required: [ id, type, state, identities, taxIds, addresses ]
      properties:
        id:
          type: string
          format: uuid
          example: 'c2a6b8e4-1f23-4a9d-8e77-9d4b5f6a7c81'
        name:
          type: string
          nullable: true
          example: 'Acme Ltd'
        email:
          type: string
          nullable: true
          example: 'billing@acme.example'
        type:
          $ref: '#/components/schemas/CustomerTypeCode'
        notes:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true
          additionalProperties: true
        state:
          type: string
          enum: [ ACTIVE, ARCHIVED ]
        externalId:
          type: string
          nullable: true
          description: The customer's ID in your own systems (its identity at the API).
          example: 'acme'
        identities:
          type: array
          items:
            $ref: '#/components/schemas/CustomerIdentityV1'
        taxIds:
          type: array
          description: Every tax ID the customer has held; current ones have no `validTill`.
          items:
            $ref: '#/components/schemas/CustomerTaxIdV1'
        addresses:
          type: array
          items:
            $ref: '#/components/schemas/CustomerAddressEntryV1'

    CustomerIdentityV1:
      type: object
      required: [ connectionId, integration, externalId ]
      properties:
        connectionId:
          type: string
          format: uuid
        integration:
          type: string
          description: The integration's slug, as on goodvat.com/integrations (`shopify`, `stripe`, `api`).
          example: 'stripe'
        externalId:
          type: string
          example: 'cus_Q1w2e3'

    CustomerTaxIdV1:
      type: object
      required: [ id, number ]
      properties:
        id:
          type: string
          format: uuid
        number:
          type: string
          description: Upper case, without spaces, hyphens or dots.
          example: 'GB123456789'
        registrationScheme:
          type: string
          nullable: true
          description: The code of the scheme that issues numbers of its format; null when none (or several) do.
          example: 'GB_VAT'
        validFrom:
          type: string
          format: date
          nullable: true
          description: The first day it applies; null when unknown (given when the customer was created).
        validTill:
          type: string
          format: date
          nullable: true
          description: The day it stopped applying; null while it applies.

    CustomerAddressEntryV1:
      type: object
      required: [ kind, address ]
      properties:
        kind:
          type: string
          enum: [ BILLING, SHIPPING ]
        isDefault:
          type: boolean
          description: The customer's default address of its kind.
        address:
          $ref: '#/components/schemas/CustomerAddressV1'

    CustomerCreationRequestV1:
      type: object
      description: Needs at least one of `name`, `email` or `externalId`.
      properties:
        name:
          type: string
          nullable: true
          example: 'Acme Ltd'
        email:
          type: string
          nullable: true
          description: Unique within the organisation.
          example: 'billing@acme.example'
        type:
          $ref: '#/components/schemas/CustomerTypeCode'
        notes:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true
          additionalProperties: true
        externalId:
          type: string
          nullable: true
          description: The customer's ID in your own systems; unique.
          example: 'acme'
        taxIds:
          type: array
          description: The tax IDs it holds. Must be empty for `PERSONAL` and `DISABLED_INDIVIDUAL` customers, who hold none.
          items:
            type: string
          example: [ 'GB123456789' ]
        addresses:
          type: array
          items:
            $ref: '#/components/schemas/CustomerAddressEntryV1'

    CustomerCreationResponseV1:
      type: object
      required:
        - customer
      properties:
        customer:
          $ref: '#/components/schemas/CustomerV1'

    CustomerUpdateRequestV1:
      type: object
      description: >
        Partial-update payload. Only `id` is required; a field omitted (or `null`) is left unchanged. `taxIds` and
        `addresses`, when given, replace the current ones: a tax ID no longer listed is ended today, never deleted, and a
        new one starts today.
      required:
        - id
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
        type:
          $ref: '#/components/schemas/CustomerTypeCode'
        notes:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true
          additionalProperties: true
        externalId:
          type: string
          nullable: true
        taxIds:
          type: array
          nullable: true
          items:
            type: string
        addresses:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/CustomerAddressEntryV1'

    CustomerUpdateResponseV1:
      type: object
      required:
        - customer
      properties:
        customer:
          $ref: '#/components/schemas/CustomerV1'

    CustomerPageV1:
      description: A page of customers.
      allOf:
        - $ref: '#/components/schemas/PageV1'
        - type: object
          required: [ content ]
          properties:
            content:
              type: array
              items:
                $ref: '#/components/schemas/CustomerV1'

    SellerAddressTypeCode:
      type: string
      description: >
        Describes the location; it does not restrict where it can be used. `HEAD_OFFICE`: where the business is established, the place its central administration is run from (one active per legal entity; not a correspondence, agent or fiscal-representative address). `OFFICE`: another establishment (a branch, shop or fixed establishment with the staff and resources to make the supplies). `WAREHOUSE`: where goods are stored and ship from (own, third-party or marketplace fulfilment). `PEOPLE`: where employees, contractors or representatives work without an office. Head offices and offices decide where the seller is based.
      enum:
        - HEAD_OFFICE
        - OFFICE
        - WAREHOUSE
        - PEOPLE
      example: 'WAREHOUSE'

    MarketplaceCode:
      type: string
      description: An online marketplace that collects tax on its sellers' sales (deemed supplier / marketplace facilitator). `OTHER` covers marketplaces not listed.
      enum:
        - AMAZON
        - EBAY
        - ETSY
        - WALMART
        - OTHER
      example: 'AMAZON'

    SellerAddressStateCode:
      type: string
      description: >
        `ACTIVE`: can be given to new transactions, and be the default supplied-from location. `RETIRED`: kept on the transactions that already use it; can no longer be given to new ones.
      enum:
        - ACTIVE
        - RETIRED
      example: 'ACTIVE'

    SellerAddressV1:
      type: object
      required:
        - id
        - type
        - state
        - address
        - defaultSuppliedFrom
        - legalEntityId
      properties:
        id:
          type: string
          format: uuid
          example: '3f2a1b0c-9d8e-4f7a-b6c5-d4e3f2a1b0c9'
        type:
          $ref: '#/components/schemas/SellerAddressTypeCode'
        state:
          $ref: '#/components/schemas/SellerAddressStateCode'
        address:
          $ref: '#/components/schemas/CustomerAddressV1'
        defaultSuppliedFrom:
          type: boolean
          description: Whether this is the origin of the legal entity's transactions and calculations that give none.
        legalEntityId:
          type: string
          format: uuid
          description: The legal entity the location belongs to.
        presentFrom:
          type: string
          format: date
          nullable: true
          description: First day the entity is present here (inclusive).
        presentTill:
          type: string
          format: date
          nullable: true
          description: Day the entity stops being present here (exclusive).

    SellerAddressCreationRequestV1:
      type: object
      required:
        - type
        - address
      properties:
        type:
          $ref: '#/components/schemas/SellerAddressTypeCode'
        address:
          allOf:
            - $ref: '#/components/schemas/CustomerAddressV1'
          description: >
            `name` and `country` are required; the rest as precise as the location's use needs (a region places people, a ship-from address needs the street). An address with identical content (case-insensitive, trimmed) is reused rather than duplicated.
        defaultSuppliedFrom:
          type: boolean
          nullable: true
          default: false
          description: >
            Makes this the origin of transactions and calculations that give none: where goods ship from by default, or, for a seller of services only, where they are supplied from. Replaces the legal entity's current default.
        legalEntityId:
          type: string
          format: uuid
          nullable: true
          description: The legal entity the location belongs to. Omitted, the organisation's default legal entity (created, named after the organisation, if it has none).
        presentFrom:
          type: string
          format: date
          nullable: true
          description: First day the entity is present here (inclusive).
        presentTill:
          type: string
          format: date
          nullable: true
          description: Day the entity stops being present here (exclusive). Must be after `presentFrom`.

    SellerAddressCreationResponseV1:
      type: object
      required:
        - sellerAddress
      properties:
        sellerAddress:
          $ref: '#/components/schemas/SellerAddressV1'

    SellerAddressUpdateRequestV1:
      type: object
      description: >
        Partial-update payload. Only `id` is required; omitted (or `null`) fields are left unchanged. The address and the legal entity cannot change: retire the location and create a new one. A retired location cannot be updated.
      required:
        - id
      properties:
        id:
          type: string
          format: uuid
        type:
          $ref: '#/components/schemas/SellerAddressTypeCode'
        defaultSuppliedFrom:
          type: boolean
          nullable: true
          description: >-
            `true` makes this the default supplied-from location (replacing the legal entity's current one), `false`
            clears it.
        presentTill:
          type: string
          format: date
          nullable: true
          description: Day the entity stops being present here (exclusive).

    SellerAddressUpdateResponseV1:
      type: object
      required:
        - sellerAddress
      properties:
        sellerAddress:
          $ref: '#/components/schemas/SellerAddressV1'

    SellerAddressPageV1:
      description: A page of seller addresses.
      allOf:
        - $ref: '#/components/schemas/PageV1'
        - type: object
          required: [ content ]
          properties:
            content:
              type: array
              items:
                $ref: '#/components/schemas/SellerAddressV1'

    RegistrationThresholdCheckV1:
      type: object
      description: Where the organisation stands today against a registration threshold.
      properties:
        registrationScheme:
          type: string
          example: 'UK VAT'
        taxAuthority:
          type: string
          example: 'HM Revenue and Customs (HMRC)'
        sellerLocation:
          type: string
          description: >
            `BASED_INSIDE_JURISDICTION` when one of the default legal entity's active locations (of a type the scheme counts as presence, e.g. `HEAD_OFFICE` or `OFFICE` for UK VAT) is in the scheme's area; otherwise `BASED_OUTSIDE_JURISDICTION`. Decides which threshold applies.
          enum: [ BASED_INSIDE_JURISDICTION, BASED_OUTSIDE_JURISDICTION ]
        status:
          type: string
          description: >
            `BELOW` (under 80%), `APPROACHING` (80% or more), `EXCEEDED` (the organisation must register), `REQUIRED_FROM_FIRST_SALE` (zero threshold, e.g. a business not established in the UK), or `REGISTERED` (a registration under the scheme is valid today).
          enum: [ BELOW, APPROACHING, EXCEEDED, REQUIRED_FROM_FIRST_SALE, REGISTERED ]
        thresholdAmount:
          $ref: '#/components/schemas/MoneyV1'
        thresholdTransactionCount:
          type: integer
          nullable: true
          description: Number of sales that meets the threshold, when it counts sales.
        combinator:
          type: string
          enum: [ ANY_OF, ALL_OF ]
          description: Whether the amount or the transaction count meets the threshold (`ANY_OF`), or only both together (`ALL_OF`).
        period:
          type: string
          enum: [ ROLLING_12_MONTHS, CURRENT_OR_PREVIOUS_CALENDAR_YEAR ]
        includesMarketplaceSales:
          type: boolean
          description: Whether sales on which a marketplace collected the tax count.
        windowFrom:
          type: string
          format: date
          description: First day measured.
        windowUntil:
          type: string
          format: date
          description: Last day measured (for a calendar-year threshold, the year closest to the threshold).
        sales:
          $ref: '#/components/schemas/MoneyV1'
        transactions:
          type: integer
          format: int64
          description: Recorded sales (not refunds) delivered into the scheme's jurisdictions.
        transactionsInOtherCurrencies:
          type: integer
          format: int64
          description: Sales not counted towards `sales` because they are in another currency than the threshold.

    TaxRegistrationV1:
      type: object
      description: A legal entity's registration under a registration scheme.
      required:
        - id
        - registrationScheme
        - registrationSchemeKind
        - taxAuthority
        - country
        - legalEntityId
        - sellerImporterOfRecord
      properties:
        id:
          type: string
          format: uuid
          example: '7b1d2c3e-4f50-4a6b-8c7d-9e0f1a2b3c4d'
        registrationScheme:
          type: string
          description: Name of the scheme registered under.
          example: 'UK VAT'
        taxAuthority:
          type: string
          description: The authority the scheme is registered with.
          example: 'HM Revenue and Customs (HMRC)'
        country:
          type: string
          description: ISO 3166-1 alpha-3 code of the authority's country.
          example: 'gbr'
        registrationNumber:
          type: string
          nullable: true
          description: The number the authority issued; null while it is applied for.
          example: 'GB123456789'
        registrationSchemeKind:
          type: string
          enum: [ STANDARD, SIMPLIFIED, ONE_STOP_SHOP ]
          description: >
            `STANDARD`: the jurisdiction's regular registration. `SIMPLIFIED`: a collect-only registration for sellers based outside (e.g. Norway VOEC). `ONE_STOP_SHOP`: one registration filing for several jurisdictions (EU Union OSS, non-Union OSS, IOSS); its number is not a tax ID of those jurisdictions' countries.
        legalEntityId:
          type: string
          format: uuid
          description: The legal entity registered.
        sellerImporterOfRecord:
          type: boolean
          description: Whether the legal entity imports goods into the scheme's jurisdictions in its own name (DDP).
          example: false
        validFrom:
          type: string
          format: date
          nullable: true
          example: '2024-04-01T00:00:00Z'
        validTill:
          type: string
          format: date
          nullable: true
        additionalInformation:
          type: string
          nullable: true
        metadata:
          type: object
          additionalProperties: true

    TaxRegistrationCreationRequestV1:
      type: object
      required:
        - registrationScheme
      properties:
        registrationScheme:
          type: string
          description: Name of an existing registration scheme.
          example: 'UK VAT'
        registrationNumber:
          type: string
          nullable: true
          description: The number the authority issued. Omitted while it is applied for.
          example: 'GB123456789'
        legalEntityId:
          type: string
          format: uuid
          nullable: true
          description: >
            The legal entity registered. Omitted, the organisation's default legal entity. Where the seller is based comes from the entity's locations, not the registration.
        sellerImporterOfRecord:
          type: boolean
          nullable: true
          default: false
          description: >
            Whether the organisation imports goods into the jurisdiction in its own name (delivered duty paid, DDP). Default false: the customer imports (DAP), so imported UK consignments above £135 carry no VAT at checkout. A calculation request's `importerOfRecord` overrides it.
        validFrom:
          type: string
          format: date
          nullable: true
          description: Start of the registration (inclusive). Omitted means open-ended.
        validTill:
          type: string
          format: date
          nullable: true
          description: End of the registration (exclusive), e.g. the deregistration date. Must be after `validFrom`.
        additionalInformation:
          type: string
          nullable: true
          maxLength: 1024
        metadata:
          type: object
          nullable: true
          additionalProperties: true

    TaxRegistrationCreationResponseV1:
      type: object
      required:
        - taxRegistration
      properties:
        taxRegistration:
          $ref: '#/components/schemas/TaxRegistrationV1'

    TaxRegistrationUpdateRequestV1:
      type: object
      description: >
        Partial-update payload. Only `id` is required; omitted (or `null`) fields are left unchanged. The registration scheme and the legal entity cannot change: create a new registration instead.
      required:
        - id
      properties:
        id:
          type: string
          format: uuid
        registrationNumber:
          type: string
          nullable: true
        sellerImporterOfRecord:
          type: boolean
          nullable: true
        validFrom:
          type: string
          format: date
          nullable: true
        validTill:
          type: string
          format: date
          nullable: true
        additionalInformation:
          type: string
          nullable: true
        metadata:
          type: object
          nullable: true
          additionalProperties: true
          description: Replaces the whole metadata object.

    TaxRegistrationUpdateResponseV1:
      type: object
      required:
        - taxRegistration
      properties:
        taxRegistration:
          $ref: '#/components/schemas/TaxRegistrationV1'

    TaxRegistrationPageV1:
      description: A page of tax registrations.
      allOf:
        - $ref: '#/components/schemas/PageV1'
        - type: object
          required: [ content ]
          properties:
            content:
              type: array
              items:
                $ref: '#/components/schemas/TaxRegistrationV1'

security:
  - bearerAuth: []
paths:
  /reference/product-category:
    get:
      summary: Search product categories
      description: >
        Searches the product categories by name, code and the words sellers use for them (synonyms, acronyms, the products a category covers), for autocomplete pickers. Every word of `query` must match: a whole word, plural or singular (`magazine` finds both magazine categories), the start of a word (`boo` finds books, e-books and audiobooks), text inside a word from four letters on (`book` finds `Audiobooks`), or, when nothing matches literally, a word with a typo (`magzine`). Case, accents and punctuation are ignored, and words run together match apart (`ebook`, `e-book` and `e book` all find `E-books`).


        Results come best match first: a query equal to a category's name or code, then a whole word in its name, then a word in its tags, then partial matches; ties by name. Without `query`, every category is returned by name. Names and descriptions come in the `locale` language, and `query` matches the names in that language and the search terms in every supported language. Japanese queries match inside words, as Japanese has no spaces (`電話` finds `携帯電話`). Pagination follows the standard `page`/`size` query parameters; `sort` is ignored. Public: no API key required. Responses are cacheable for an hour.
      operationId: searchProductCategories
      tags:
        - Reference Data
      security: []
      parameters:
        - in: query
          name: query
          required: false
          schema:
            type: string
            maxLength: 100
          description: What the seller typed. Omit, or leave blank, to list every category.
          example: 'ebook'
        - in: query
          name: locale
          required: false
          schema:
            type: string
            default: en
          description: The language of the display text, as a language tag (`en`, `en-GB`); only the language counts. A language not supported yet, or a malformed tag, gets English, never an error. The response's `Content-Language` header names the language used. Supported today - `en`, `es`, `fr`, `ja`.
          example: 'en'
        - in: query
          name: page
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
        - in: query
          name: size
          required: false
          schema:
            type: integer
            minimum: 1
            default: 20
      responses:
        '200':
          description: The matching categories, best match first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCategoryPageV1'
        '400':
          description: '`query` is longer than 100 characters.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
              example:
                errors:
                  - "query must be at most 100 characters"
  /product:
    post:
      summary: Create a product
      description: >
        Creates a product the organisation sells. Its `externalId` (else its `sku`) is its ID at the API and must be unique: reusing one returns `409 Conflict`. Requires the `api:product:create` authority.
      operationId: createProductApi
      tags:
        - Product
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductCreationRequestV1'
            examples:
              Physical_Good:
                summary: A physical good with your own ID and SKU
                value:
                  name: 'Black Ceramic Mug'
                  description: '350ml matte black ceramic mug'
                  productCategory: 'GOODS_GEN'
                  externalId: 'mug-blk-001'
                  sku: 'MUG-BLK-001'
              SaaS_Subscription_Product:
                summary: A SaaS plan
                value:
                  name: 'Pro Plan (Monthly)'
                  productCategory: 'SW_SAAS'
                  externalId: 'pro-monthly'
      responses:
        '201':
          description: Product created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCreationResponseV1'
        '400':
          description: Invalid request payload (e.g. blank `name`, unknown `productCategory`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:product:create` authority.
        '409':
          description: A product with this `externalId` (or `sku` standing in for one) already exists for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
              example:
                errors:
                  - "Product with SKU MUG-BLK-001 already exists"
    get:
      summary: Get a product by id, or list products for the organisation
      description: >
        A single endpoint covering both lookup and listing. Pass `id` to fetch one product by UUID; omit it to receive a paginated, organisation-scoped list instead — pagination follows standard Spring Data `page`/`size`/`sort` query parameters. Requires the `api:product:read` authority.


        When listing, `name` and `sku` are optional, mutually exclusive, case-insensitive partial-match (substring) filters, scoped to the caller's organisation. Supplying both in the same request returns `400 Bad Request` — pick one.
      operationId: getOrListProductsApi
      tags:
        - Product
      parameters:
        - in: query
          name: id
          required: false
          schema:
            type: string
            format: uuid
          description: UUID of a single product to fetch. When present, all pagination parameters are ignored and the response body is a single `ProductV1` object rather than a page.
          example: 'b1e6c9a2-8f34-4e2d-9c11-6a2f8d7e4b10'
        - in: query
          name: name
          required: false
          schema:
            type: string
          description: Case-insensitive partial match on product name, scoped to the organisation. Ignored when `id` is supplied. Mutually exclusive with `sku` — supplying both returns `400 Bad Request`.
          example: 'ceramic mug'
        - in: query
          name: sku
          required: false
          schema:
            type: string
          description: Case-insensitive partial match on a SKU at any of the product's connections, scoped to the organisation. Ignored when `id` is supplied. Mutually exclusive with `name` — supplying both returns `400 Bad Request`.
          example: 'MUG-BLK'
        - in: query
          name: page
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
          description: Zero-based page index to return. Ignored when `id` is supplied.
        - in: query
          name: size
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            default: 20
          description: Number of products per page. Ignored when `id` is supplied.
        - in: query
          name: sort
          required: false
          schema:
            type: string
          description: Sort order, as `property,(asc|desc)` (e.g. `name,asc`). Repeatable for multi-field sort. Ignored when `id` is supplied.
          example: 'name,asc'
      responses:
        '200':
          description: Either a single product (when `id` is supplied) or a page of products for the organisation.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ProductV1'
                  - $ref: '#/components/schemas/ProductPageV1'
        '400':
          description: Both `name` and `sku` were supplied — they are mutually exclusive; supply at most one.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:product:read` authority.
        '404':
          description: No product exists with the given `id` for the organisation. Only possible when `id` is supplied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    patch:
      summary: Partially update a product
      description: >
        Applies a partial update to an existing product — only non-null fields on the request are applied; omit (or send `null` for) any field that should be left unchanged. `id` is always required to identify the target product. Changing `sku` is subject to the same per-organisation uniqueness check as creation. Requires the `api:product:update` authority.
      operationId: updateProductApi
      tags:
        - Product
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductUpdateRequestV1'
            examples:
              Restock_And_Rename:
                summary: Update stock and name only, leaving everything else untouched
                value:
                  id: 'b1e6c9a2-8f34-4e2d-9c11-6a2f8d7e4b10'
                  name: 'Black Ceramic Mug (Matte)'
                  stock: 750
      responses:
        '200':
          description: Product updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductUpdateResponseV1'
        '400':
          description: Invalid request payload (e.g. missing `id`, blank `name`, unknown `productCategory`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:product:update` authority.
        '404':
          description: No product exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '409':
          description: The new `externalId` already belongs to a different product in the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /product/{id}:
    delete:
      summary: Delete a product
      description: >
        Archives the product: it stays on the transactions that reference it, with state `ARCHIVED`. Requires the `api:product:delete` authority.
      operationId: deleteProductApi
      tags:
        - Product
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
          description: UUID of the product to delete.
          example: 'b1e6c9a2-8f34-4e2d-9c11-6a2f8d7e4b10'
      responses:
        '204':
          description: Product deleted successfully.
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:product:delete` authority.
        '404':
          description: No product exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /customer:
    post:
      summary: Create a customer
      description: >
        Creates a new customer belonging to the authenticated organisation. `name`, `type`, and `email` are mandatory; `platform` defaults to `OTHER` when omitted. `email` must be unique within the organisation. If `address` is supplied and an address with identical content (case-insensitive, trimmed) already exists, that existing address is reused rather than creating a duplicate row. Requires the `api:customer:create` authority.
      operationId: createCustomerApi
      tags:
        - Customer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerCreationRequestV1'
            examples:
              Business_Customer:
                summary: Business customer with an address and a discount
                value:
                  name: 'Acme Corp'
                  type: 'BUSINESS'
                  platform: 'SHOPIFY'
                  taxId: 'GB123456789'
                  discount:
                    type: 'PERCENTAGE'
                    value: 10
                  email: 'billing@acmecorp.com'
                  address:
                    name: 'Head Office'
                    country: 'GBR'
                    region: 'London'
                    postalCode: 'SW1A 1AA'
                    city: 'London'
                    line1: '10 Downing St'
      responses:
        '201':
          description: Customer created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerCreationResponseV1'
        '400':
          description: Invalid request payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:customer:create` authority.
        '409':
          description: A customer with this `email` or `externalId` already exists for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    get:
      summary: Get a customer by id, or list customers for the organisation
      description: >
        A single endpoint covering both lookup and listing. Pass `id` to fetch one customer by UUID; omit it to receive a paginated, organisation-scoped list instead — pagination follows standard Spring Data `page`/`size`/`sort` query parameters. Requires the `api:customer:read` authority.
      operationId: getOrListCustomersApi
      tags:
        - Customer
      parameters:
        - in: query
          name: id
          required: false
          schema:
            type: string
            format: uuid
          description: UUID of a single customer to fetch. When present, the response body is a single `CustomerV1` object rather than a page.
          example: 'c2a6b8e4-1f23-4a9d-8e77-9d4b5f6a7c81'
        - in: query
          name: page
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
          description: Zero-based page index. Ignored when `id` is supplied.
        - in: query
          name: size
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            default: 20
          description: Page size. Ignored when `id` is supplied.
        - in: query
          name: sort
          required: false
          schema:
            type: string
          description: Sort order, as `property,(asc|desc)`. Repeatable. Ignored when `id` is supplied.
          example: 'name,asc'
      responses:
        '200':
          description: Either a single customer (when `id` is supplied) or a page of customers.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CustomerV1'
                  - $ref: '#/components/schemas/CustomerPageV1'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:customer:read` authority.
        '404':
          description: No customer exists with the given `id` for the organisation. Only possible when `id` is supplied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    patch:
      summary: Partially update a customer
      description: >
        Applies a partial update to an existing customer — only non-null fields on the request are applied. `id` is always required. If `address` is supplied, an existing matching address row is reused rather than creating a duplicate. Requires the `api:customer:update` authority.
      operationId: updateCustomerApi
      tags:
        - Customer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomerUpdateRequestV1'
      responses:
        '200':
          description: Customer updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerUpdateResponseV1'
        '400':
          description: Invalid request payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:customer:update` authority.
        '404':
          description: No customer exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '409':
          description: The new `email` already belongs to a different customer in the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /customer/{id}:
    delete:
      summary: Delete a customer
      description: >
        Archives the customer: it stays on the transactions that reference it, with state `ARCHIVED`. Requires the `api:customer:delete` authority.
      operationId: deleteCustomerApi
      tags:
        - Customer
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
          description: UUID of the customer to delete.
          example: 'c2a6b8e4-1f23-4a9d-8e77-9d4b5f6a7c81'
      responses:
        '204':
          description: Customer deleted successfully.
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:customer:delete` authority.
        '404':
          description: No customer exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /seller-address:
    post:
      summary: Create a seller address
      description: >
        Adds a location of one of the organisation's legal entities. Requires the `api:seller-address:create` authority.
      operationId: createSellerAddressApi
      tags:
        - Seller Address
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SellerAddressCreationRequestV1'
            examples:
              Warehouse:
                summary: A warehouse that is the default supplied-from location
                value:
                  type: 'WAREHOUSE'
                  address:
                    name: 'Shenzhen fulfilment centre'
                    country: 'CHN'
                    city: 'Shenzhen'
                  defaultSuppliedFrom: true
      responses:
        '201':
          description: Seller address created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SellerAddressCreationResponseV1'
        '400':
          description: Invalid request payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:seller-address:create` authority.
        '409':
          description: The organisation already has this address with this `type`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    get:
      summary: Get a seller address by id, or list the organisation's seller addresses
      description: >
        Pass `id` to fetch one seller address; omit it for a paginated list (Spring Data `page`/`size`/`sort` query parameters), optionally filtered by `state`. Requires the `api:seller-address:read` authority.
      operationId: getOrListSellerAddressesApi
      tags:
        - Seller Address
      parameters:
        - in: query
          name: id
          required: false
          schema:
            type: string
            format: uuid
        - in: query
          name: state
          required: false
          schema:
            $ref: '#/components/schemas/SellerAddressStateCode'
          description: Only list seller addresses in this state. Ignored when `id` is supplied.
        - in: query
          name: page
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
        - in: query
          name: size
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            default: 20
        - in: query
          name: sort
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Either a single seller address (when `id` is supplied) or a page of seller addresses.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/SellerAddressV1'
                  - $ref: '#/components/schemas/SellerAddressPageV1'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:seller-address:read` authority.
        '404':
          description: No seller address exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    patch:
      summary: Partially update a seller address
      description: >
        Changes the type or the default flag of an active seller address; the address itself cannot change. Requires the `api:seller-address:update` authority.
      operationId: updateSellerAddressApi
      tags:
        - Seller Address
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SellerAddressUpdateRequestV1'
      responses:
        '200':
          description: Seller address updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SellerAddressUpdateResponseV1'
        '400':
          description: Invalid request payload, or the seller address is retired.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:seller-address:update` authority.
        '404':
          description: No seller address exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '409':
          description: The organisation already has this address with this `type`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /seller-address/{id}:
    delete:
      summary: Retire a seller address
      description: >
        Retires the seller address: it stays on the transactions and tax registrations that already use it, but can no longer be given to new ones, and stops being the default supplied-from address. Retiring a retired address does nothing. Requires the `api:seller-address:delete` authority.
      operationId: deleteSellerAddressApi
      tags:
        - Seller Address
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Seller address deleted successfully.
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:seller-address:delete` authority.
        '404':
          description: No seller address exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /tax-registration:
    post:
      summary: Create a tax registration
      description: >
        Records that the organisation is registered under a registration scheme. From then on, tax calculations for the organisation in the scheme's jurisdictions return tax for transaction dates within the registration's validity. At most one registration per scheme may be valid at any time. Requires the `api:tax-registration:create` authority.
      operationId: createTaxRegistrationApi
      tags:
        - Tax Registration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaxRegistrationCreationRequestV1'
            examples:
              UK_VAT_Overseas_Seller:
                summary: A US business registered for UK VAT
                value:
                  registrationScheme: 'UK VAT'
                  registrationNumber: 'GB123456789'
                  validFrom: '2024-04-01'
              Non_Union_OSS:
                summary: A US business registered for the EU non-Union OSS in Ireland
                value:
                  registrationScheme: 'Non-Union OSS (Ireland)'
                  registrationNumber: 'EU372000041'
                  validFrom: '2025-01-01'
      responses:
        '201':
          description: Tax registration created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaxRegistrationCreationResponseV1'
        '400':
          description: Invalid request payload, unknown `registrationScheme`, a closed legal entity, or `validTill` not after `validFrom`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:tax-registration:create` authority.
        '409':
          description: Another registration of the organisation under this scheme is valid during the same period.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    get:
      summary: Get a tax registration by id, or list the organisation's tax registrations
      description: >
        Pass `id` to fetch one registration; omit it for a paginated list (Spring Data `page`/`size`/`sort` query parameters). Requires the `api:tax-registration:read` authority.
      operationId: getOrListTaxRegistrationsApi
      tags:
        - Tax Registration
      parameters:
        - in: query
          name: id
          required: false
          schema:
            type: string
            format: uuid
          description: UUID of a single registration to fetch. When present, the response body is a single `TaxRegistrationV1`.
        - in: query
          name: page
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
            default: 0
        - in: query
          name: size
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            default: 20
        - in: query
          name: sort
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Either a single registration (when `id` is supplied) or a page of registrations.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TaxRegistrationV1'
                  - $ref: '#/components/schemas/TaxRegistrationPageV1'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:tax-registration:read` authority.
        '404':
          description: No registration exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
    patch:
      summary: Partially update a tax registration
      description: >
        Applies the non-null fields of the request, e.g. setting `validTill` when the organisation deregisters. Requires the `api:tax-registration:update` authority.
      operationId: updateTaxRegistrationApi
      tags:
        - Tax Registration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaxRegistrationUpdateRequestV1'
      responses:
        '200':
          description: Tax registration updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaxRegistrationUpdateResponseV1'
        '400':
          description: Invalid request payload, or `validTill` not after `validFrom`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:tax-registration:update` authority.
        '404':
          description: No registration exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '409':
          description: The new validity period overlaps another registration under the same scheme.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'

  /tax-registration/thresholds:
    get:
      summary: Check registration thresholds
      description: >
        For every registration threshold in force, where the organisation stands today. Sales are the organisation's transactions recorded in GoodVat, attributed by delivery address to the scheme's jurisdictions, with refunds subtracted; sales in other currencies are counted separately. Thresholds never change what is calculated: registrations do. Requires the `api:tax-registration:read` authority.
      operationId: checkRegistrationThresholdsApi
      tags:
        - Tax Registration
      responses:
        '200':
          description: One check per registration scheme with a threshold in force.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RegistrationThresholdCheckV1'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:tax-registration:read` authority.

  /tax-registration/{id}:
    delete:
      summary: Delete a tax registration
      description: >
        Deletes a registration nothing has been collected under. One that a transaction collected tax under can't be deleted (400): record the deregistration by setting `validTill` instead, which keeps the history. Requires the `api:tax-registration:delete` authority.
      operationId: deleteTaxRegistrationApi
      tags:
        - Tax Registration
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Tax registration deleted successfully.
        '400':
          description: A transaction collected tax under the registration; end it with `validTill` instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Missing or invalid API key.
        '403':
          description: Authenticated but the API credential is missing the `api:tax-registration:delete` authority.
        '404':
          description: No registration exists with the given `id` for the organisation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
