openapi: 3.0.3
servers:
  - url: https://api.verifacti.com/
info:
  title: Documentación de la API TicketBai
  contact:
    email: info@verifacti.com
    name: Contacto
    url: https://www.verifacti.com
  description: |

    &nbsp;

    Aquí encontraras la documentación de la API de TicketBai, el sistema análogo a Verifactu para el País Vasco. Es una API separada completamente de
    la API de Verifactu aunque es muy similar. Los únicos cambios significativos se encuentran en los métodos para crear y modificar facturas. Esto
    se debe a que las propias APIs de cada hacienda son diferentes. Sin embargo, la estructura general de estos métodos es la misma.

    &nbsp;

    La documentación de la API de Verifactu se encuentra <a href="https://www.verifacti.com/docs">aquí</a>.

    &nbsp;

    Asimismo, hemos creado una tercera API para poder gestionar los diferentes NIFs de manera programática. Para usuarios que solo deben gestionar
    unos pocos NIFs, esto se puede hacer de manera cómoda a través de nuestra interfaz. Sin embargo, en ocasiones esto no es práctico y hace falta tener 
    una manera programática de dar de alta y de baja diferentes NIFs. Para ello, se puede utilizar nuestra API de gestión de NIFs, cuya documentación se
    puede consultar <a href="https://www.verifacti.com/nifs-docs">aquí</a>. Esta API está incluida en el precio y se puede usar por cualquier usuario
    con una suscripción activa.

    &nbsp;

    <a
      href="https://storage.googleapis.com/verifacti_non_sensitive/postman_ticketbai.json"
      download="tb-docs.json"
      style="
      display: inline-block;
      padding: 6px 12px;
      background-color: #FF6C37;
      color: white;
      text-decoration: none;
      border-radius: 5px;
      font-weight: bold;
      font-family: Arial, sans-serif;
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
    ">
      Colección de Postman
    </a>

    # Comparativa entre las APIs de las haciendas vascas y la API de Verifacti

    Las APIs que las haciendas vascas han puesto a disposición de los contribuyentes para enviar las facturas requieren seguir los siguientes pasos:

    &nbsp;

    <ol style="list-style: decimal;">
      <li>Crear una huella digital usando los datos de la factura y el NIF del contribuyente.</li>
      <li>Componer un fichero XML con los datos de la factura, la huella y la huella del envío anterior.</li>
      <li>Firmar digitalmente el fichero XML usando un formato XAdES-EPES.</li>
      <li>Enviar dicho fichero XML a la hacienda correspondiente mediante una llamada autenticada usando un certificado electrónico cualificado reconocido.</li>
      <li>En paralelo, se ha de generar un código QR que contiene la url de verificación de la factura así como el código TicketBai para incluir en la factura que se entrega al destinatario.</li>
      <li>Guardar los datos de cada factura.</li>
    </ol>

    &nbsp;

    Usando nuestra API de TicketBai, todos estos pasos se simplifican en un solo paso:

    &nbsp;

    <ol style="list-style: decimal;">
      <li>Hacer una llamada (sin certificado) con los datos de la factura en formato JSON.</li>
    </ol>

    &nbsp;

    Nuestra API te responde de inmediato con el código QR y el código TickerBai para que los añadas directamente en la factura. Nosotros nos encargamos de
    la huella, los ficheros XML, la firma digital, el código QR, el código TicketBai, la llamada autenticada y el almacenaje de las facturas.

    &nbsp;

    En cualquier momento puedes comprobar el estado de todos tus envíos y facturas via API. Además, dispondrás de un sencillo dashboard
    para hacerlo de forma más cómoda. Esto puede resultar especialmente útil en el momento de hacer las primeras pruebas e integración. Ahí
    también encontrarás ejemplos de las distintas llamadas e incluso una terminal para que puedas hacer pruebas sin necesidad de usar
    POSTMAN, por ejemplo.

    # Idempotencia

    Los endpoints de mutación (`/create`, `/modify` y `/cancel`) aceptan una cabecera opcional
    `Idempotency-Key` para hacer reintentos seguros tras fallos de red. La clave es una cadena ASCII
    imprimible de 1 a 255 caracteres y se recuerda 24 horas por NIF. Los detalles completos y códigos de error (`400`, `409`, `422`) se documentan en
    cada endpoint afectado.
paths:
  /ticketbai/health:
    get:
      summary: Estado API
      description: |
        Este endpoint devuelve el estado de la API key. Sirve principalmente para comprobar que la API está activa y para saber a qué NIF, entorno y hacienda hace referencia.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl 'https://api.verifacti.com/ticketbai/health' \
            --header 'Authorization: Bearer <API_KEY>'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/health'
            headers = {
              'Authorization': 'Bearer <API_KEY>'
            }
            response = requests.get(url, headers=headers)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/health';
            const headers = {
              'Authorization': 'Bearer <API_KEY>'
            };
            fetch(url, { headers })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const response = await axios.get("https://api.verifacti.com/ticketbai/health", {
              headers: {
                "Authorization": "Bearer <API_KEY>"
              },
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/health";

            $headers = [
                "Authorization: Bearer <API_KEY>",
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, err := http.NewRequest(\"GET\", \"https://api.verifacti.com/ticketbai/health\", nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/health"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .GET()
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var response = await client.GetAsync("https://api.verifacti.com/ticketbai/health");

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/health"

            oHttp.Open "GET", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"

            oHttp.send

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      responses:
        "200":
          description: Estado de la API
          content:
            application/json:
              schema:
                type: object
                properties:
                  estado:
                    description: Estado de la API.
                    type: string
                    example: OK
                  nif:
                    description: NIF del contribuyente.
                    type: string
                    example: B75777847
                  entorno:
                    description: Entorno de la API key.
                    type: string
                    example: test
                  hacienda:
                    description: Hacienda correspondiente al NIF.
                    type: string
                    enum:
                      - alava
                      - guipuzcoa
                      - vizcaya
                    example: alava
  /ticketbai/status:
    post:
      summary: Estado factura
      description: |
        Este endpoint permite consultar el estado de una factura tal y como está registrada en nuestro sistema.
        Para consultar el estado de registros de facturación, se debe consultar el endpoint GET `/status`.

        &nbsp;

        Para obtener el estado de cualquier factura emitida, se pueden visitar las siguientes direcciones de la Hacienda correspondiente:

        <ul style="list-style: disc; margin-top: 6px;">
          <li><a href="https://ticketbai.araba.eus/tbai/consultafacturas/?idioma=es">Álava</a></li>
          <li><a href="https://www.gipuzkoa.eus/es/web/ogasuna/ticketbai/consulta-facturas">Guipuzcoa</a></li>
          <li><a href="https://www.batuz.eus/es/lroe">Vizcaya</a></li>
        </ul>

        En el entorno de test, los datos de las facturas se guardarán un máximo de 90 días.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request POST 'https://api.verifacti.com/ticketbai/status' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data '{
                "serie": "A",
                "numero": "234634",
                "fecha_expedicion": "CURRENT_DATE"
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/status'
            headers = {
              'Authorization': 'Bearer <API_KEY>'
            }
            data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE'
            }
            response = requests.post(url, headers=headers, json=data)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/status';
            const headers = {
              'Authorization': 'Bearer <API_KEY>'
            };
            const data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE'
            };
            fetch(url, { headers, method: 'POST', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE'
            };

            const response = await axios.post("https://api.verifacti.com/ticketbai/status", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/status";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
                "serie": "A",
                "numero": "234634",
                "fecha_expedicion": "CURRENT_DATE"
            }
            JSON;

            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n    \"serie\": \"A\",\n    \"numero\": \"234634\",\n    \"fecha_expedicion\": \"CURRENT_DATE\"\n}\n`\n\n\treq, err := http.NewRequest(\"POST\", \"https://api.verifacti.com/ticketbai/status\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                                "serie": "A",
                                "numero": "234634",
                                "fecha_expedicion": "CURRENT_DATE"
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/status"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
                ""serie"": ""A"",
                ""numero"": ""234634"",
                ""fecha_expedicion"": ""CURRENT_DATE""
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PostAsync("https://api.verifacti.com/ticketbai/status", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/status"

            oHttp.Open "POST", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "    ""serie"": ""A""," & vbCrLf & _
                   "    ""numero"": ""234634""," & vbCrLf & _
                   "    ""fecha_expedicion"": ""CURRENT_DATE""" & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - serie
                - numero
                - fecha_expedicion
              properties:
                serie:
                  type: string
                  example: A
                  maxLength: 20
                  description: Serie de la factura.
                numero:
                  type: string
                  minLength: 1
                  maxLength: 20
                  pattern: (\s*[^\s]\s*)+
                  example: "234634"
                  description: Número de la factura.
                fecha_expedicion:
                  type: string
                  pattern: ^\d{2}-\d{2}-\d{4}$
                  example: CURRENT_DATE
                  description: Fecha de emisión de la factura. No puede ser una fecha posterior a la actual.
      responses:
        "200":
          description: Estado factura
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/estadoFactura"
        "500":
          description: Error de servidor
    get:
      summary: Estado registro
      description: |
        Este endpoint permite conocer el estado actual de un registro de facturación efectuado anteriormente. Inmediatamente después de enviar un registro, éste se
        encuentra siempre en estado `Pendiente`, se encola y se procesará típicamente en unos pocos segundos.

        &nbsp;

        Es importante entender que, aunque no es lo normal, pueden existir múltiples registros de facturación para una misma factura. Por ejemplo, una factura creada
        y anulada posteriormente tendría dos registros de facturación diferentes (uno para la creación y otro para la anulación). Este endpoint devuelve el estado del
        registro de facturación, que no corresponde necesariamente con el estado de la factura.

        &nbsp;

        En el entorno de test, los datos relativos a los registros de facturación se guardarán un máximo de 90 días.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl 'https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c' \
            --header 'Authorization: Bearer <API_KEY>'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c'
            headers = {
              'Authorization': 'Bearer <API_KEY>'
            }
            response = requests.get(url, headers=headers)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c';
            const headers = {
              'Authorization': 'Bearer <API_KEY>'
            };
            fetch(url, { headers })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const response = await axios.get("https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c", {
              headers: {
                "Authorization": "Bearer <API_KEY>"
              },
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c";

            $headers = [
                "Authorization: Bearer <API_KEY>",
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n)\n\nfunc main() {\n\treq, err := http.NewRequest(\"GET\", \"https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c\", nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .GET()
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var response = await client.GetAsync("https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c");

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/status?uuid=b018ced3-b362-4494-8776-9eefff1c160c"

            oHttp.Open "GET", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"

            oHttp.send

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      parameters:
        - in: query
          name: uuid
          required: true
          schema:
            type: string
          example: b018ced3-b362-4494-8776-9eefff1c160c
          description: Identificador único del registro recibido como respuesta en el momento del envío.
      responses:
        "200":
          description: Estado registro
          content:
            application/json:
              schema:
                type: object
                properties:
                  nif:
                    description: NIF del emisor de la factura.
                    type: string
                  serie:
                    description: Serie de la factura.
                    type: string
                  numero:
                    description: Número de la factura.
                    type: string
                  fecha_expedicion:
                    description: Fecha de emisión de la factura.
                    type: string
                  operacion:
                    type: string
                    description: |
                      En general hay cinco tipos de operaciones:
                      <ol style="list-style: disc;">
                        <li>Alta: envío normal de una factura. Endpoint: <code>/create</code>.</li>
                        <li>Modificacion: modificación de una factura. Endpoint: <code>/modify</code> con `accion` igual a MODIFICAR. No es posible en Vizcaya.</li>
                        <li>Subsanacion: subsanación de una factura. Endpoint: <code>/modify</code> con `accion` igual a SUBSANAR. No es posible en Vizcaya.</li>
                        <li>Anulacion: anulación normal de una factura. Endpoint: Endpoint: <code>/cancel</code>.</li>
                        <li>Anulación (rechazo previo): anulación de una factura que ha sido rechazada previamente. Endpoint: <code>/cancel</code> con `rechazo_previo=true`. No es posible en Vizcaya.</li>
                      </ol>
                  estado:
                    description: |
                      Estado del registro de facturación:
                      <ol style="list-style: disc;">
                        <li>Pendiente: Registro encolado y no procesado aún.</li>
                        <li>Correcto: Registro procesado correctamente por la hacienda correspondiente.</li>
                        <li>Aceptado con errores: Registro aceptado con errores.</li>
                        <li>Incorrecto: Registro considerado incorrecto.</li>
                        <li>Anulada: Registro de anulación procesado correctamente por la hacienda correspondiente.</li>
                        <li>Error servidor Hacienda: Error en el servidor de Hacienda. Se intentará reenviar el registros de facturación de nuevo.</li>
                      </ol>
                    type: string
                  url:
                    description: URL de verificación del código QR.
                    type: string
                  qr:
                    description: Código QR en base 64 que contiene la url.
                    type: string
                  tbai:
                    description: Código TicketBai.
                    type: string
                  mensaje_error:
                    description: Descripcion del error tal y como aparece la hacienda correspondiente.
                    type: string
                example:
                  nif: A15022510
                  serie: A
                  numero: "34547"
                  fecha_expedicion: CURRENT_DATE
                  operacion: Alta
                  url: https://pruebas-ticketbai.araba.eus/tbai/qrtbai/?id=TBAI-A15022510-021224-mwUG7NRo2ZNxp-010&s=A&nf=34547&i=242&cr=010
                  qr: jBBWRw0KGgoABAANSUhEAH0CAIAAABE...
                  tbai: TBAI-B05434202-021224-mwUG7NRo2ZNxp-010
                  estado: Correcto
        "404":
          description: Registro no encontrado
        "500":
          description: Error de servidor
  /ticketbai/create:
    post:
      summary: Crear factura nueva
      description: |
        Mediante este endpoint se crea un registro de facturación nuevo. Existen tres estados de respuesta y es importante entender qué ocurre en cada caso:
        <br><br>
        <ol style="list-style: disc;">
          <li>
            <strong>200</strong>: Nuestra API acepta la petición y la encola para ser procesada. En este caso, la respuesta contiene el código QR en base 64, el código TicketBai
            y la URL de verificación que contiene el código QR. Además, se devuelve el estado del registro, que siempre será <code style="color: black;">Pendiente</code>, lo que
            indica que ha sido encolado pero aún no procesado.
            <br>
            Es importante entender que este estado únicamente indica que el registro de facturación se generará y se enviará a hacienda, lo cual no significa que necesariamente
            hacienda lo acepte. Nuestra API realiza numerosas validaciones para minimizar el riesgo de que esto ocurra pero puede ocurrir.
            <br>
            El estado del envío se puede consultar con el endpoint GET <code style="color: black;">/status</code> usando el <code style="color: black;">uuid</code> devuelto. También disponemos de webhooks para recibir notificaciones acerca
            de los estados de los registros de facturación. La documentación de estos webhooks se puede
            consultar <a href='https://www.verifacti.com/nifs-docs#tag/Webhooks' target='_blank'>aquí</a>.
            <br><br>
          </li>
          <li>
            <strong>400</strong>: Nuestra API rechaza la llamada por algún error en el JSON enviado. El registro de facturación no se genera y no se producirá ninguna comunicación con hacienda.
            <br>
            Realizamos numerosas comprobaciones para minimizar el riesgo de que hacienda rechace el envío. En la respuesta se indicará un mensaje con la descripción del error.
            Las validaciones van desde comprobaciones simples como que no falte ningún campo requerido hasta más complejas como que el NIF del destinatario esté censado en la AEAT.
            <br><br>
          </li>
          <li>
            <strong>500</strong>: Error del servidor. Este es un error genérico que no debería ocurrir. En caso de que ocurra, el registro de facturación no se genera y,
            por lo tanto, no se produce ningún envío a hacienda. El cliente deberá reintentar la llamada más tarde.  
          </li>
        </ol>

        &nbsp;

        La documentación completa del endpoint correspondiente para la API de la AEAT se puede consultar
        <a href='https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/Validaciones_Errores_Veri-Factu.pdf' target='_blank'>aquí</a>.

        &nbsp;

        Este endpoint admite además una cabecera opcional `Idempotency-Key`. Si se incluye, los reintentos de la misma petición lógica con la misma clave
        replican la respuesta original (incluyendo la cabecera de respuesta `Idempotent-Replayed: true` y el
        eco de `Idempotency-Key`). Las claves se vinculan al NIF y se recuerdan durante 24 horas.
        Reusar la misma clave con un cuerpo diferente devuelve 422; un reintento concurrente mientras el
        primero sigue en curso devuelve 409. Consulta la sección "Idempotencia" al inicio de esta documentación
        para más detalles.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request POST 'https://api.verifacti.com/ticketbai/create' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data-raw '{
              "serie": "A",
              "numero": "234634",
              "fecha_expedicion": "CURRENT_DATE",
              "simplificada": false,
              "nif": "A15022510",
              "nombre": "Empresa de ejemplo SL",
              "direccion": "direccion cliente",
              "cp": "48992",
              "descripcion": "descripcion de la factura",
              "tipo_operacion": "servicios",
              "lineas": [{
                  "descripcion": "linea 1",
                  "cantidad": "2",
                  "importe_unitario": "100.00",
                  "importe_total": "242.00"
              }],
              "desglose_iva": [
                {
                  "base_imponible": "200",
                  "tipo_impositivo": "21",
                  "cuota_impuesto": "42"
                }
              ],
              "importe_total": "242.00"
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/create'
            headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            }
            data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE',
              'simplificada': False,
              'nif': 'A15022510',
              'nombre': 'Empresa de ejemplo SL',
              'direccion': 'direccion cliente',
              'cp': '48992',
              'descripcion': 'descripcion de la factura',
              'tipo_operacion': 'servicios',
              'lineas': [
                {
                  'descripcion': 'linea 1',
                  'cantidad': "2",
                  'importe_unitario': "100.00",
                  'importe_total': "242.00"
                }
              ],
              'desglose_iva': [
                {
                  'base_imponible': "200",
                  'tipo_impositivo': "21",
                  'cuota_impuesto': "42"
                }
              ],
              'importe_total': "242.00"
            }
            response = requests.post(url, headers=headers, json=data)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/create';
            const headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            };
            const data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE',
              'simplificada': false,
              'nif': 'A15022510',
              'nombre': 'Empresa de ejemplo SL',
              'direccion': 'direccion cliente',
              'cp': '48992',
              'descripcion': 'descripcion de la factura',
              'tipo_operacion': 'servicios',
              'lineas': [
                {
                  'descripcion': 'linea 1',
                  'cantidad': '2',
                  'importe_unitario': '100.00',
                  'importe_total': '242.00'
                }
              ],
              'desglose_iva': [
                {
                  'base_imponible': "200",
                  'tipo_impositivo': "21",
                  'cuota_impuesto': "42"
                }
              ],
              'importe_total': '242.00'
            };
            fetch(url, { headers, method: 'POST', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE',
              'simplificada': false,
              'nif': 'A15022510',
              'nombre': 'Empresa de ejemplo SL',
              'direccion': 'direccion cliente',
              'cp': '48992',
              'descripcion': 'descripcion de la factura',
              'tipo_operacion': 'servicios',
              'lineas': [
                {
                  'descripcion': 'linea 1',
                  'cantidad': '2',
                  'importe_unitario': '100.00',
                  'importe_total': '242.00'
                }
              ],
              'desglose_iva': [
                {
                  'base_imponible': "200",
                  'tipo_impositivo': "21",
                  'cuota_impuesto': "42"
                }
              ],
              'importe_total': '242.00'
            };

            const response = await axios.post("https://api.verifacti.com/ticketbai/create", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/create";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
              "serie": "A",
              "numero": "234634",
              "fecha_expedicion": "CURRENT_DATE",
              "simplificada": false,
              "nif": "A15022510",
              "nombre": "Empresa de ejemplo SL",
              "direccion": "direccion cliente",
              "cp": "48992",
              "descripcion": "descripcion de la factura",
              "tipo_operacion": "servicios",
              "lineas": [{
                  "descripcion": "linea 1",
                  "cantidad": "2",
                  "importe_unitario": "100.00",
                  "importe_total": "242.00"
              }],
              "desglose_iva": [
                {
                  "base_imponible": "200",
                  "tipo_impositivo": "21",
                  "cuota_impuesto": "42"
                }
              ],
              "importe_total": "242.00"
            }
            JSON;

            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n  \"serie\": \"A\",\n  \"numero\": \"234634\",\n  \"fecha_expedicion\": \"CURRENT_DATE\",\n  \"simplificada\": false,\n  \"nif\": \"A15022510\",\n  \"nombre\": \"Empresa de ejemplo SL\",\n  \"direccion\": \"direccion cliente\",\n  \"cp\": \"48992\",\n  \"descripcion\": \"descripcion de la factura\",\n  \"tipo_operacion\": \"servicios\",\n  \"lineas\": [{\n      \"descripcion\": \"linea 1\",\n      \"cantidad\": \"2\",\n      \"importe_unitario\": \"100.00\",\n      \"importe_total\": \"242.00\"\n  }],\n  \"desglose_iva\": [\n    {\n      \"base_imponible\": \"200\",\n      \"tipo_impositivo\": \"21\",\n      \"cuota_impuesto\": \"42\"\n    }\n  ],\n  \"importe_total\": \"242.00\"\n}\n`\n\n\treq, err := http.NewRequest(\"POST\", \"https://api.verifacti.com/ticketbai/create\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                              "serie": "A",
                              "numero": "234634",
                              "fecha_expedicion": "CURRENT_DATE",
                              "simplificada": false,
                              "nif": "A15022510",
                              "nombre": "Empresa de ejemplo SL",
                              "direccion": "direccion cliente",
                              "cp": "48992",
                              "descripcion": "descripcion de la factura",
                              "tipo_operacion": "servicios",
                              "lineas": [{
                                  "descripcion": "linea 1",
                                  "cantidad": "2",
                                  "importe_unitario": "100.00",
                                  "importe_total": "242.00"
                              }],
                              "desglose_iva": [
                                {
                                  "base_imponible": "200",
                                  "tipo_impositivo": "21",
                                  "cuota_impuesto": "42"
                                }
                              ],
                              "importe_total": "242.00"
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/create"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
              ""serie"": ""A"",
              ""numero"": ""234634"",
              ""fecha_expedicion"": ""CURRENT_DATE"",
              ""simplificada"": false,
              ""nif"": ""A15022510"",
              ""nombre"": ""Empresa de ejemplo SL"",
              ""direccion"": ""direccion cliente"",
              ""cp"": ""48992"",
              ""descripcion"": ""descripcion de la factura"",
              ""tipo_operacion"": ""servicios"",
              ""lineas"": [{
                  ""descripcion"": ""linea 1"",
                  ""cantidad"": ""2"",
                  ""importe_unitario"": ""100.00"",
                  ""importe_total"": ""242.00""
              }],
              ""desglose_iva"": [
                {
                  ""base_imponible"": ""200"",
                  ""tipo_impositivo"": ""21"",
                  ""cuota_impuesto"": ""42""
                }
              ],
              ""importe_total"": ""242.00""
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PostAsync("https://api.verifacti.com/ticketbai/create", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/create"

            oHttp.Open "POST", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "  ""serie"": ""A""," & vbCrLf & _
                   "  ""numero"": ""234634""," & vbCrLf & _
                   "  ""fecha_expedicion"": ""CURRENT_DATE""," & vbCrLf & _
                   "  ""simplificada"": false," & vbCrLf & _
                   "  ""nif"": ""A15022510""," & vbCrLf & _
                   "  ""nombre"": ""Empresa de ejemplo SL""," & vbCrLf & _
                   "  ""direccion"": ""direccion cliente""," & vbCrLf & _
                   "  ""cp"": ""48992""," & vbCrLf & _
                   "  ""descripcion"": ""descripcion de la factura""," & vbCrLf & _
                   "  ""tipo_operacion"": ""servicios""," & vbCrLf & _
                   "  ""lineas"": [{" & vbCrLf & _
                   "      ""descripcion"": ""linea 1""," & vbCrLf & _
                   "      ""cantidad"": ""2""," & vbCrLf & _
                   "      ""importe_unitario"": ""100.00""," & vbCrLf & _
                   "      ""importe_total"": ""242.00""" & vbCrLf & _
                   "  }]," & vbCrLf & _
                   "  ""desglose_iva"": [" & vbCrLf & _
                   "    {" & vbCrLf & _
                   "      ""base_imponible"": ""200""," & vbCrLf & _
                   "      ""tipo_impositivo"": ""21""," & vbCrLf & _
                   "      ""cuota_impuesto"": ""42""" & vbCrLf & _
                   "    }" & vbCrLf & _
                   "  ]," & vbCrLf & _
                   "  ""importe_total"": ""242.00""" & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          description: Clave única opcional para garantizar que reintentos de la misma petición no creen duplicados. Cadena ASCII imprimible de 1 a 255 caracteres. Se recuerda durante 24 horas por NIF.
          schema:
            type: string
            pattern: '^[\x20-\x7E]{1,255}$'
            minLength: 1
            maxLength: 255
            example: 8e1b9c2a-3f4d-4a6b-9c2e-1d2f3e4a5b6c
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - serie
                - numero
                - fecha_expedicion
                - descripcion
                - lineas
                - importe_total
              properties:
                serie:
                  type: string
                  maxLength: 20
                  description: Serie de la factura.
                numero:
                  type: string
                  minLength: 1
                  maxLength: 20
                  pattern: (\s*[^\s]\s*)+
                  description: Número de la factura.
                fecha_expedicion:
                  type: string
                  pattern: \d{2,2}-\d{2,2}-\d{4,4}
                  description: |
                    Fecha de emisión de la factura que <span style="color: red">debe ser la fecha actual</span>. En caso de querer emitir una factura por una operación que ocurrió en una fecha pasada se debe usar el campo `fecha_operacion`.
                descripcion:
                  type: string
                  minLength: 1
                  maxLength: 250
                  description: Descripción de la factura.
                simplificada:
                  type: boolean
                  default: false
                  description: |
                    Indica si la factura es simplificada. En caso de no ser simplificada se debe incluir el campo `nif` o `id_otro`. Además, se deben incluir los datos
                    del destinatario de la factura `nombre`, `cp` y `direccion`.
                nif:
                  type: string
                  example: A15022510
                  description: |
                    NIF del cliente al que se le emite la factura. Necesario salvo para facturas simplificadas o cuando se incluya el campo `id_otro`.
                    En caso de incluir tanto `nif` como `id_otro`, se utilizará `nif`.

                    &nbsp;

                    Si se incluye el campo `nif` validamos por defecto que está censado en la AEAT ya que si no lo está, la Hacienda Foral Vasca rechazará el envío. En caso de
                    no estar censado, se devolverá un error 400, lo que significa que el registro de facturación no se ha generado y no se producirá envío a la Hacienda Foral Vasca.

                    &nbsp;

                    Para personas jurídicas es suficiente con que el NIF esté censado. Para personas físicas, es necesario que el NIF esté censado y que el `nombre` sea
                    suficientemente parecido al nombre que aparece en el censo.

                    &nbsp;

                    Disponemos de un endpoint para validar el NIF de personas fisicas y jurídicas en la API de gestión de NIFs. Se puede consultar la documentación
                    <a href="https://www.verifacti.com/nifs-docs#tag/NIFs/paths/~1nifs~1validar/post" target="_blank">aquí</a>.
                id_otro:
                  type: object
                  description: |
                    Identificador de persona física o jurídica distinto del NIF. Necesario salvo para facturas simplificadas o cuando se incluya el campo `nif`.
                    En caso de incluir tanto `nif` como `id_otro`, se utilizará `nif`.
                  properties:
                    codigo_pais:
                      type: string
                      description: Código del país de la persona física o jurídica en formato ISO 3166-1 alpha-2. Parámetro opcional.
                    id_type:
                      type: string
                      enum:
                        - "02"
                        - "03"
                        - "04"
                        - "05"
                        - "06"
                      description: |
                        Tipo de identificador de la persona física o jurídica. Los diferentes tipos de identificador son:
                        <ol style="list-style: disc;">
                          <li>02: NIF-IVA</li>
                          <li>03: Pasaporte</li>
                          <li>04: Documento oficial de identificación expedido por el país o territorio de residencia</li>
                          <li>05: Certificado de residencia</li>
                          <li>06: Otro documento probatorio</li>
                        </ol> 
                    id:
                      type: string
                      maxLength: 20
                      description: |
                        Identificador de la persona física o jurídica. Se permite hasta un máximo de 20 caracteres.

                        &nbsp;

                        Cuando el país es intracomunitario y el `id_type` es igual a `02`, es decir, corresponde a un número de IVA, se validará por defecto
                        que esté en el censo VIES. Si no lo está, la Hacienda Foral Vasca rechazará el envío.

                        &nbsp;

                        Disponemos de un endpoint para validación de IVAs en el censo VIES en la API de gestión de NIFs. Se
                        puede consultar la documentación <a href="https://www.verifacti.com/nifs-docs#tag/NIFs/paths/~1nifs~1validar~1vies/post" target="_blank">aquí</a>.
                nombre:
                  type: string
                  maxLength: 120
                  description: Nombre y apellidos o razón social del cliente al que se le emite la factura. Necesario salvo para facturas simplificadas.
                validar_destinatario:
                  type: boolean
                  default: true
                  description: |
                    Si se incluye el campo `nif` validamos por defecto que está censado en la AEAT ya que si no lo está, la Hacienda Foral Vasca rechazará el envío. Esto requiere una
                    llamada adicional por nuestro lado. Puedes desactivar esta validación en el entorno de test poniendo este campo a `false`.

                    &nbsp;

                    De manera similar, si se incluye el campo `id_otro` con `id_type` igual a `02` y `codigo_pais` de un país intracomunitario, validamos por defecto que
                    el IVA esté en el censo VIES. Se puede desactivar esta validación en el entorno de test poniendo este campo a `false`.
                cp:
                  type: string
                  maxLength: 20
                  description: Código postal del cliente al que se le emite la factura.  Necesario salvo para facturas simplificadas en Álava y Guipúzcoa.
                direccion:
                  type: string
                  maxLength: 250
                  description: Dirección del cliente al que se le emite la factura. Necesario salvo para facturas simplificadas en Álava y Guipúzcoa.
                sustitucion_simplificada:
                  type: boolean
                  default: false
                  description: Indica si la factura es emitida en sustitución de una factura simplificada.
                tipo_operacion:
                  type: string
                  enum:
                    - servicios
                    - bienes
                  default: servicios
                  description: |
                    Tipo de operación. Los diferentes tipos de operación son:
                    <ol style="list-style: disc;">
                      <li>servicios: prestación de servicios</li>
                      <li>bienes: entrega de bienes</li>
                lineas:
                  type: array
                  minItems: 1
                  maxItems: 1000
                  description: Líneas de la factura.
                  items:
                    type: object
                    example:
                      descripcion: Venta de zapatos
                      cantidad: "2"
                      importe_unitario: "100.00"
                      importe_total: "242.00"
                    required:
                      - descripcion
                      - cantidad
                      - importe_unitario
                      - importe_total
                    properties:
                      descripcion:
                        type: string
                        minLength: 1
                        maxLength: 250
                        description: Descripción de la línea.
                      cantidad:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Cantidad de la línea.
                      importe_unitario:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Importe unitario SIN IVA de la línea de factura.
                      descuento:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Importe en euros SIN IVA del descuento de la línea de factura.
                      importe_total:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Importe total CON IVA de la línea de factura.
                desglose_iva:
                  description: Desglose del IVA.
                  maxItems: 12
                  type: array
                  items:
                    type: object
                    properties:
                      base_imponible:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                        description: Base imponible no exenta. Sobre la base imponible se aplica el tipo impositivo. Es obligatorio salvo para facturas exentas.
                      tipo_impositivo:
                        type: string
                        pattern: \d{1,3}(\.\d{0,2})?
                        description: Porcentaje aplicado sobre la base imponible para calcular la cuota. Es obligatorio salvo para facturas exentas.
                      cuota_impuesto:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                        description: Cuota repercutida. Será la cuota resultante de aplicar a la base imponible el tipo impositivo. Es obligatorio salvo para facturas exentas.
                      tipo_recargo_equivalencia:
                        description: Porcentaje asociado en función del tipo de IVA.
                        type: string
                        pattern: \d{1,3}(\.\d{0,2})?
                      cuota_recargo_equivalencia:
                        description: Cuota resultante de aplicar a la base imponible el tipo de recargo de equivalencia.
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      recargo_equivalencia_o_regimen_simplificado:
                        description: Recargo de equivalencia o regimen simplificado.
                        type: boolean
                        default: false
                detalle_renta:
                  description: Permitido y obligatorio únicamente para personas físicas en Vizcaya.
                  maxItems: 10
                  type: array
                  items:
                    type: object
                    properties:
                      epigrafe:
                        type: string
                        pattern: "[0-9]*"
                        description: Campo obligatorio. El listado de epigrafes se puede descargar <a target="_blank" href="https://www.batuz.eus/descargar_documento.asp?url=fitxategiak/batuz%2Flroe%2Fbatuz_lroe_lista_epigrafes_v1_0_3.xlsx&param=1">aquí</a>.
                      ingreso_a_computar_irpf_diferente_base_impo_iva:
                        type: string
                        enum:
                          - S
                          - "N"
                        description: Campo opcional.
                      importe_ingreso_irpf:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                        description: Campo opcional.
                importe_total:
                  type: string
                  pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                  description: Importe total de la factura.
                fecha_operacion:
                  type: string
                  pattern: \d{2,2}-\d{2,2}-\d{4,4}
                  description: Fecha de la operación.
                rectificativa:
                  type: object
                  description: Campo que incluye los datos necesarios en caso de que la factura sea rectificativa.
                  properties:
                    codigo:
                      type: string
                      description: |
                        Código de factura rectificativa Los diferentes valores son:
                        <ol style="list-style: disc;">
                          <li>R1: Factura rectificativa: error fundado en derecho y Art. 80 Uno, Dos y Seis de la Norma Foral del IVA</li>
                          <li>R2: Factura rectificativa: artículo 80 Tres de la Norma Foral del IVA</li>
                          <li>R3: Factura rectificativa: artículo 80 Cuatro de la Norma Foral del IVA</li>
                          <li>R4: Factura rectificativa: Resto</li>
                          <li>R5: Factura rectificativa en facturas simplificadas</li>
                        </ol>
                    tipo:
                      type: string
                      enum:
                        - S
                        - I
                      description: Indica si la factura es rectificativa por sustitución (`S`) o por diferencias (`I`).
                    base_rectificada:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Base imponible de la factura sustituida.
                    cuota_rectificada:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Cuota repercutida de la factura sustituida.
                    cuota_recargo:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Cuota del recargo de equivalencia de la factura sustituida.
                rectificadas_sustituidas:
                  type: array
                  maxItems: 100
                  description: Facturas rectificadas o sustituidas.
                  items:
                    type: object
                    properties:
                      serie:
                        type: string
                        maxLength: 20
                        description: Serie de la factura rectificada o sustituida.
                      numero:
                        type: string
                        minLength: 1
                        maxLength: 20
                        pattern: (\s*[^\s]\s*)+
                        description: Número de la factura rectificada o sustituida.
                      fecha_expedicion:
                        type: string
                        pattern: \d{2,2}-\d{2,2}-\d{4,4}
                        description: Fecha de emisión de la factura rectificada o sustituida.
                no_sujeta:
                  description: Indica si la factura es no sujeta.
                  type: object
                  properties:
                    causa:
                      type: string
                      description: |
                        Causa de la no sujeción. Los posibles valores son:
                        <ol style="list-style: disc;">
                          <li>OT: No sujeto por el artículo 7 de la Norma Foral de IVA Otros supuestos de no sujeción</li>
                          <li>RL: No sujeto por reglas de localización</li>
                          <li>VT: No sujeto, ventas realizadas por cuenta de terceros (importe no computable a efectos de IVA ni de IRPF)</li>
                          <li>IE: No sujeto en el TAI por reglas de localización, pero repercute impuesto extranjero, IPS/IGIC o IVA de otro estado miembro UE</li>
                        </ol>
                    importe:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Importe en euros correspondiente a la operación no sujeta.
                exenta:
                  description: Indica si la factura es exenta.
                  type: object
                  properties:
                    causa_exencion:
                      type: string
                      description: |
                        Causa de la exención. Los posibles valores son:
                        <ol style="list-style: disc;">
                          <li>E1: Exenta por el artículo 20 de la Norma Foral del IVA</li>
                          <li>E2: Exenta por el artículo 21 de la Norma Foral del IVA</li>
                          <li>E3: Exenta por el artículo 22 de la Norma Foral del IVA</li>
                          <li>E4: Exenta por los artículos 23 y 24 de la Norma Foral del IVA</li>
                          <li>E5: Exenta por el artículo 25 de la Norma Foral del IVA</li>
                          <li>E6: Exenta por otra causa</li>
                        </ol>
                    base_imponible:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Base imponible exenta en euros correspondiente a la causa de exención.
                especial:
                  type: object
                  description: Aquí se recogen campos opcionales adicionales.
                  properties:
                    inversion_sujeto_pasivo:
                      type: boolean
                      default: false
                      description: Con inversión del sujeto pasivo.
                    retencion_soportada:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Retención soportada.
                    base_imponible_a_coste:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Base imponible a coste (para grupos de IVA – nivel avanzado).
                    clave_regimen:
                      type: string
                      default: "01"
                      enum:
                        - "01"
                        - "02"
                        - "03"
                        - "04"
                        - "05"
                        - "06"
                        - "07"
                        - "08"
                        - "09"
                        - "10"
                        - "11"
                        - "12"
                        - "13"
                        - "14"
                        - "15"
                        - "17"
                        - "19"
                        - "51"
                        - "52"
                        - "53"
                        - "54"
                      description: Clave de régimen especial de IVA y operaciones con trascendencia tributaria.
              example:
                serie: A
                numero: "234634"
                fecha_expedicion: CURRENT_DATE
                simplificada: false
                nif: A15022510
                nombre: Empresa de ejemplo SL
                direccion: direccion cliente
                cp: "48992"
                descripcion: descripcion de la factura
                tipo_operacion: servicios
                lineas:
                  - descripcion: linea 1
                    cantidad: "2"
                    importe_unitario: "100.00"
                    importe_total: "242.00"
                desglose_iva:
                  - base_imponible: "200.00"
                    tipo_impositivo: "21"
                    cuota_impuesto: "42"
                importe_total: "242.00"
      responses:
        "200":
          description: Factura creada
          content:
            application/json:
              schema:
                type: object
                properties:
                  uuid:
                    description: Identificador único del registro para poder consultar su estado.
                    type: string
                    example: ea6eae92-bac6-48c3-baa4-2bbf6b5ee562
                  tbai:
                    description: Código TicketBai.
                    type: string
                    example: TBAI-B05434202-021224-mwUG7NRo2ZNxp-010
                  estado:
                    description: Estado del registro. Siempre será igual a "Pendiente" como respuesta a una creación. Dicho estado se puede consultar posteriormente con el endpoint `/status`.
                    type: string
                    example: Pendiente
                  url:
                    description: URL de verificación del código QR.
                    type: string
                    example: https://pruebas-ticketbai.araba.eus/tbai/qrtbai/?id=TBAI-B05434202-021224-mwUG7NRo2ZNxp-010&s=A&nf=234634&i=242&cr=010
                  qr:
                    description: Código QR en base 64 que contiene la url.
                    type: string
                    example: jBBWRw0KGgoABAANSUhEAH0CAIAAABE...
        "500":
          description: Error de servidor
  /ticketbai/modify:
    put:
      summary: Modificar o subsanar factura
      description: |
        Este endpoint ha sido habilitado por las haciendas vascas de Álava y Guipúzcoa para el envío de la información correspondiente al fichero
        TicketBAI que ha sido rechazado y/o recibido con aviso de errores por no cumplir con los requisitos y condiciones establecidos en la
        normativa que regula la obligación TicketBAI. Es parte de lo que se conoce como el servicio ZUZENDU.

        Los campos son exactamente los mismos que los incluidos en el endpoint de crear factura, con el añadido del campo `accion` que indica si
        se quiere modificar o subsanar la factura.

        &nbsp;

        <span style="color: red">IMPORTANTE</span>: únicamente disponible para Álava y Guipúzcoa.

        &nbsp;

        Este endpoint admite además una cabecera opcional `Idempotency-Key`. Si se incluye, los reintentos de la misma petición lógica con la misma clave
        replican la respuesta original (incluyendo la cabecera de respuesta `Idempotent-Replayed: true` y el
        eco de `Idempotency-Key`). Las claves se vinculan al NIF y se recuerdan durante 24 horas.
        Reusar la misma clave con un cuerpo diferente devuelve 422; un reintento concurrente mientras el
        primero sigue en curso devuelve 409.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request PUT 'https://api.verifacti.com/ticketbai/modify' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data-raw '{
              "accion": "SUBSANAR",
              "serie": "A",
              "numero": "234634",
              "fecha_expedicion": "CURRENT_DATE",
              "simplificada": false,
              "nif": "A15022510",
              "nombre": "Empresa de ejemplo SL",
              "direccion": "direccion cliente",
              "cp": "48992",
              "descripcion": "descripcion de la factura",
              "tipo_operacion": "servicios",
              "lineas": [{
                  "descripcion": "linea 1",
                  "cantidad": "2",
                  "importe_unitario": "100.00",
                  "importe_total": "242.00"
              }],
              "desglose_iva": [{
                  "base_imponible": "200",
                  "tipo_impositivo": "21",
                  "cuota_impuesto": "42"
              }],
              "importe_total": "242.00"
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/create'
            headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            }
            data = {
              'accion': 'SUBSANAR',
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE',
              'simplificada': False,
              'nif': 'A15022510',
              'nombre': 'Empresa de ejemplo SL',
              'direccion': 'direccion cliente',
              'cp': '48992',
              'descripcion': 'descripcion de la factura',
              'tipo_operacion': 'servicios',
              'lineas': [
                {
                  'descripcion': 'linea 1',
                  'cantidad': '2',
                  'importe_unitario': '100.00',
                  'importe_total': '242.00'
                }
              ],
              'desglose_iva': [
                {
                  'base_imponible': '200',
                  'tipo_impositivo': '21',
                  'cuota_impuesto': '42'
                }
              ],
              'importe_total': '242.00'
            }
            response = requests.put(url, headers=headers, json=data)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/create';
            const headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            };
            const data = {
              'accion': 'SUBSANAR',
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE',
              'simplificada': false,
              'nif': 'A15022510',
              'nombre': 'Empresa de ejemplo SL',
              'direccion': 'direccion cliente',
              'cp': '48992',
              'descripcion': 'descripcion de la factura',
              'tipo_operacion': 'servicios',
              'lineas': [
                {
                  'descripcion': 'linea 1',
                  'cantidad': '2',
                  'importe_unitario': '100.00',
                  'importe_total': '242.00'
                }
              ],
              'desglose_iva': [
                {
                  'base_imponible': '200',
                  'tipo_impositivo': '21',
                  'cuota_impuesto': '42'
                }
              ],
              'importe_total': '242.00'
            };
            fetch(url, { headers, method: 'PUT', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'accion': 'SUBSANAR',
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE',
              'simplificada': false,
              'nif': 'A15022510',
              'nombre': 'Empresa de ejemplo SL',
              'direccion': 'direccion cliente',
              'cp': '48992',
              'descripcion': 'descripcion de la factura',
              'tipo_operacion': 'servicios',
              'lineas': [
                {
                  'descripcion': 'linea 1',
                  'cantidad': '2',
                  'importe_unitario': '100.00',
                  'importe_total': '242.00'
                }
              ],
              'desglose_iva': [
                {
                  'base_imponible': '200',
                  'tipo_impositivo': '21',
                  'cuota_impuesto': '42'
                }
              ],
              'importe_total': '242.00'
            };

            const response = await axios.put("https://api.verifacti.com/ticketbai/modify", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/modify";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
              "accion": "SUBSANAR",
              "serie": "A",
              "numero": "234634",
              "fecha_expedicion": "CURRENT_DATE",
              "simplificada": false,
              "nif": "A15022510",
              "nombre": "Empresa de ejemplo SL",
              "direccion": "direccion cliente",
              "cp": "48992",
              "descripcion": "descripcion de la factura",
              "tipo_operacion": "servicios",
              "lineas": [{
                  "descripcion": "linea 1",
                  "cantidad": "2",
                  "importe_unitario": "100.00",
                  "importe_total": "242.00"
              }],
              "desglose_iva": [{
                  "base_imponible": "200",
                  "tipo_impositivo": "21",
                  "cuota_impuesto": "42"
              }],
              "importe_total": "242.00"
            }
            JSON;

            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n  \"accion\": \"SUBSANAR\",\n  \"serie\": \"A\",\n  \"numero\": \"234634\",\n  \"fecha_expedicion\": \"CURRENT_DATE\",\n  \"simplificada\": false,\n  \"nif\": \"A15022510\",\n  \"nombre\": \"Empresa de ejemplo SL\",\n  \"direccion\": \"direccion cliente\",\n  \"cp\": \"48992\",\n  \"descripcion\": \"descripcion de la factura\",\n  \"tipo_operacion\": \"servicios\",\n  \"lineas\": [{\n      \"descripcion\": \"linea 1\",\n      \"cantidad\": \"2\",\n      \"importe_unitario\": \"100.00\",\n      \"importe_total\": \"242.00\"\n  }],\n  \"desglose_iva\": [{\n      \"base_imponible\": \"200\",\n      \"tipo_impositivo\": \"21\",\n      \"cuota_impuesto\": \"42\"\n  }],\n  \"importe_total\": \"242.00\"\n}\n`\n\n\treq, err := http.NewRequest(\"PUT\", \"https://api.verifacti.com/ticketbai/modify\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                              "accion": "SUBSANAR",
                              "serie": "A",
                              "numero": "234634",
                              "fecha_expedicion": "CURRENT_DATE",
                              "simplificada": false,
                              "nif": "A15022510",
                              "nombre": "Empresa de ejemplo SL",
                              "direccion": "direccion cliente",
                              "cp": "48992",
                              "descripcion": "descripcion de la factura",
                              "tipo_operacion": "servicios",
                              "lineas": [{
                                  "descripcion": "linea 1",
                                  "cantidad": "2",
                                  "importe_unitario": "100.00",
                                  "importe_total": "242.00"
                              }],
                              "desglose_iva": [{
                                  "base_imponible": "200",
                                  "tipo_impositivo": "21",
                                  "cuota_impuesto": "42"
                              }],
                              "importe_total": "242.00"
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/modify"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .PUT(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
              ""accion"": ""SUBSANAR"",
              ""serie"": ""A"",
              ""numero"": ""234634"",
              ""fecha_expedicion"": ""CURRENT_DATE"",
              ""simplificada"": false,
              ""nif"": ""A15022510"",
              ""nombre"": ""Empresa de ejemplo SL"",
              ""direccion"": ""direccion cliente"",
              ""cp"": ""48992"",
              ""descripcion"": ""descripcion de la factura"",
              ""tipo_operacion"": ""servicios"",
              ""lineas"": [{
                  ""descripcion"": ""linea 1"",
                  ""cantidad"": ""2"",
                  ""importe_unitario"": ""100.00"",
                  ""importe_total"": ""242.00""
              }],
              ""desglose_iva"": [{
                  ""base_imponible"": ""200"",
                  ""tipo_impositivo"": ""21"",
                  ""cuota_impuesto"": ""42""
              }],
              ""importe_total"": ""242.00""
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PutAsync("https://api.verifacti.com/ticketbai/modify", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/modify"

            oHttp.Open "PUT", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "  ""accion"": ""SUBSANAR""," & vbCrLf & _
                   "  ""serie"": ""A""," & vbCrLf & _
                   "  ""numero"": ""234634""," & vbCrLf & _
                   "  ""fecha_expedicion"": ""CURRENT_DATE""," & vbCrLf & _
                   "  ""simplificada"": false," & vbCrLf & _
                   "  ""nif"": ""A15022510""," & vbCrLf & _
                   "  ""nombre"": ""Empresa de ejemplo SL""," & vbCrLf & _
                   "  ""direccion"": ""direccion cliente""," & vbCrLf & _
                   "  ""cp"": ""48992""," & vbCrLf & _
                   "  ""descripcion"": ""descripcion de la factura""," & vbCrLf & _
                   "  ""tipo_operacion"": ""servicios""," & vbCrLf & _
                   "  ""lineas"": [{" & vbCrLf & _
                   "      ""descripcion"": ""linea 1""," & vbCrLf & _
                   "      ""cantidad"": ""2""," & vbCrLf & _
                   "      ""importe_unitario"": ""100.00""," & vbCrLf & _
                   "      ""importe_total"": ""242.00""" & vbCrLf & _
                   "  }]," & vbCrLf & _
                   "  ""desglose_iva"": [{" & vbCrLf & _
                   "      ""base_imponible"": ""200""," & vbCrLf & _
                   "      ""tipo_impositivo"": ""21""," & vbCrLf & _
                   "      ""cuota_impuesto"": ""42""" & vbCrLf & _
                   "  }]," & vbCrLf & _
                   "  ""importe_total"": ""242.00""" & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          description: Clave única opcional para garantizar que reintentos de la misma petición no creen duplicados. Cadena ASCII imprimible de 1 a 255 caracteres. Se recuerda durante 24 horas por NIF.
          schema:
            type: string
            pattern: '^[\x20-\x7E]{1,255}$'
            minLength: 1
            maxLength: 255
            example: 8e1b9c2a-3f4d-4a6b-9c2e-1d2f3e4a5b6c
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - accion
                - serie
                - numero
                - fecha_expedicion
                - descripcion
                - lineas
                - importe_total
              properties:
                accion:
                  type: string
                  enum:
                    - MODIFICAR
                    - SUBSANAR
                  description: |
                    Acción a realizar. Los posibles valores son:
                    <ol style="list-style: disc;">
                      <li>
                        SUBSANAR: en caso de necesitar corregir una factura que no ha sido recibida por la hacienda correspondiente previamente en el sistema debido a un error en la creación.
                      </li>
                      <li>
                        MODIFICAR: en caso de necesitar corregir una factura que sí ha sido recibida por la hacienda correspondiente previamente.
                      </li>
                    </ol>
                serie:
                  type: string
                  maxLength: 20
                  description: Serie de la factura.
                numero:
                  type: string
                  minLength: 1
                  maxLength: 20
                  pattern: (\s*[^\s]\s*)+
                  description: Número de la factura.
                fecha_expedicion:
                  type: string
                  pattern: \d{2,2}-\d{2,2}-\d{4,4}
                  description: Fecha de emisión de la factura. No puede ser una fecha posterior a la actual.
                descripcion:
                  type: string
                  minLength: 1
                  maxLength: 250
                  description: Descripción de la factura.
                simplificada:
                  type: boolean
                  default: false
                  description: |
                    Indica si la factura es simplificada. En caso de no ser simplificada se debe incluir el campo `nif` o `id_otro`. Además, se deben incluir los datos
                    del destinatario de la factura `nombre`, `cp` y `direccion`.
                nif:
                  type: string
                  example: A15022510
                  description: |
                    NIF del cliente al que se le emite la factura. Necesario salvo para facturas simplificadas o cuando se incluya el campo `id_otro`.
                    En caso de incluir tanto `nif` como `id_otro`, se utilizará `nif`.

                    &nbsp;

                    Si se incluye el campo `nif` validamos por defecto que está censado en la AEAT ya que si no lo está, la Hacienda Foral Vasca rechazará el envío. En caso de
                    no estar censado, se devolverá un error 400, lo que significa que el registro de facturación no se ha generado y no se producirá envío a la Hacienda Foral Vasca.

                    &nbsp;

                    Para personas jurídicas es suficiente con que el NIF esté censado. Para personas físicas, es necesario que el NIF esté censado y que el `nombre` sea
                    suficientemente parecido al nombre que aparece en el censo.

                    &nbsp;

                    Disponemos de un endpoint para validar el NIF de personas fisicas y jurídicas en la API de gestión de NIFs. Se puede consultar la documentación
                    <a href="https://www.verifacti.com/nifs-docs#tag/NIFs/paths/~1nifs~1validar/post" target="_blank">aquí</a>.
                id_otro:
                  type: object
                  description: |
                    Identificador de persona física o jurídica distinto del NIF. Necesario salvo para facturas simplificadas o cuando se incluya el campo `nif`.
                    En caso de incluir tanto `nif` como `id_otro`, se utilizará `nif`.
                  properties:
                    codigo_pais:
                      type: string
                      description: Código del país de la persona física o jurídica en formato ISO 3166-1 alpha-2. Parámetro opcional.
                    id_type:
                      type: string
                      enum:
                        - "02"
                        - "03"
                        - "04"
                        - "05"
                        - "06"
                      description: |
                        Tipo de identificador de la persona física o jurídica. Los diferentes tipos de identificador son:
                        <ol style="list-style: disc;">
                          <li>02: NIF-IVA</li>
                          <li>03: Pasaporte</li>
                          <li>04: Documento oficial de identificación expedido por el país o territorio de residencia</li>
                          <li>05: Certificado de residencia</li>
                          <li>06: Otro documento probatorio</li>
                        </ol> 
                    id:
                      type: string
                      maxLength: 20
                      description: |
                        Identificador de la persona física o jurídica. Se permite hasta un máximo de 20 caracteres.

                        &nbsp;

                        Cuando el país es intracomunitario y el `id_type` es igual a `02`, es decir, corresponde a un número de IVA, se validará por defecto
                        que esté en el censo VIES. Si no lo está, la Hacienda Foral Vasca rechazará el envío.

                        &nbsp;

                        Disponemos de un endpoint para validación de IVAs en el censo VIES en la API de gestión de NIFs. Se
                        puede consultar la documentación <a href="https://www.verifacti.com/nifs-docs#tag/NIFs/paths/~1nifs~1validar~1vies/post" target="_blank">aquí</a>.
                nombre:
                  type: string
                  maxLength: 120
                  description: Nombre y apellidos o razón social del cliente al que se le emite la factura. Necesario salvo para facturas simplificadas.
                validar_destinatario:
                  type: boolean
                  default: true
                  description: |
                    Si se incluye el campo `nif` validamos por defecto que está censado en la AEAT ya que si no lo está, la Hacienda Foral Vasca rechazará el envío. Esto requiere una
                    llamada adicional por nuestro lado. Puedes desactivar esta validación en el entorno de test poniendo este campo a `false`.

                    &nbsp;

                    De manera similar, si se incluye el campo `id_otro` con `id_type` igual a `02` y `codigo_pais` de un país intracomunitario, validamos por defecto que
                    el IVA esté en el censo VIES. Se puede desactivar esta validación en el entorno de test poniendo este campo a `false`.
                cp:
                  type: string
                  maxLength: 20
                  description: Código postal del cliente al que se le emite la factura.  Necesario salvo para facturas simplificadas en Álava y Guipúzcoa.
                direccion:
                  type: string
                  maxLength: 250
                  description: Dirección del cliente al que se le emite la factura. Necesario salvo para facturas simplificadas en Álava y Guipúzcoa.
                sustitucion_simplificada:
                  type: boolean
                  default: false
                  description: Indica si la factura es emitida en sustitución de una factura simplificada.
                tipo_operacion:
                  type: string
                  enum:
                    - servicios
                    - bienes
                  default: servicios
                  description: |
                    Tipo de operación. Los diferentes tipos de operación son:
                    <ol style="list-style: disc;">
                      <li>servicios: prestación de servicios</li>
                      <li>bienes: entrega de bienes</li>
                lineas:
                  type: array
                  minItems: 1
                  maxItems: 1000
                  description: Líneas de la factura.
                  items:
                    type: object
                    example:
                      descripcion: Venta de zapatos
                      cantidad: "2"
                      importe_unitario: "100.00"
                      importe_total: "242.00"
                    required:
                      - descripcion
                      - cantidad
                      - importe_unitario
                      - importe_total
                    properties:
                      descripcion:
                        type: string
                        minLength: 1
                        maxLength: 250
                        description: Descripción de la línea.
                      cantidad:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Cantidad de la línea.
                      importe_unitario:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Importe unitario SIN IVA de la línea de factura.
                      descuento:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Importe en euros SIN IVA del descuento de la línea de factura.
                      importe_total:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,8})?
                        description: Importe total CON IVA de la línea de factura.
                desglose_iva:
                  description: Desglose del IVA.
                  maxItems: 12
                  type: array
                  items:
                    type: object
                    properties:
                      base_imponible:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                        description: Base imponible no exenta. Sobre la base imponible se aplica el tipo impositivo. Es obligatorio salvo para facturas exentas.
                      tipo_impositivo:
                        type: string
                        pattern: \d{1,3}(\.\d{0,2})?
                        description: Porcentaje aplicado sobre la base imponible para calcular la cuota. Es obligatorio salvo para facturas exentas.
                      cuota_impuesto:
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                        description: Cuota repercutida. Será la cuota resultante de aplicar a la base imponible el tipo impositivo. Es obligatorio salvo para facturas exentas.
                      tipo_recargo_equivalencia:
                        description: Porcentaje asociado en función del tipo de IVA.
                        type: string
                        pattern: \d{1,3}(\.\d{0,2})?
                      cuota_recargo_equivalencia:
                        description: Cuota resultante de aplicar a la base imponible el tipo de recargo de equivalencia.
                        type: string
                        pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      recargo_equivalencia_o_regimen_simplificado:
                        description: Recargo de equivalencia o regimen simplificado.
                        type: boolean
                        default: false
                importe_total:
                  type: string
                  pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                  description: Importe total de la factura.
                fecha_operacion:
                  type: string
                  pattern: \d{2,2}-\d{2,2}-\d{4,4}
                  description: Fecha de la operación.
                rectificativa:
                  type: object
                  description: Campo que incluye los datos necesarios en caso de que la factura sea rectificativa.
                  properties:
                    codigo:
                      type: string
                      description: |
                        Código de factura rectificativa Los diferentes valores son:
                        <ol style="list-style: disc;">
                          <li>R1: Factura rectificativa: error fundado en derecho y Art. 80 Uno, Dos y Seis de la Norma Foral del IVA</li>
                          <li>R2: Factura rectificativa: artículo 80 Tres de la Norma Foral del IVA</li>
                          <li>R3: Factura rectificativa: artículo 80 Cuatro de la Norma Foral del IVA</li>
                          <li>R4: Factura rectificativa: Resto</li>
                          <li>R5: Factura rectificativa en facturas simplificadas</li>
                        </ol>
                    tipo:
                      type: string
                      enum:
                        - S
                        - I
                      description: Indica si la factura es rectificativa por sustitución (`S`) o por diferencias (`I`).
                    base_rectificada:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Base imponible de la factura sustituida.
                    cuota_rectificada:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Cuota repercutida de la factura sustituida.
                    cuota_recargo:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Cuota del recargo de equivalencia de la factura sustituida.
                rectificadas_sustituidas:
                  type: array
                  maxItems: 100
                  description: Facturas rectificadas o sustituidas.
                  items:
                    type: object
                    properties:
                      serie:
                        type: string
                        maxLength: 20
                        description: Serie de la factura rectificada o sustituida.
                      numero:
                        type: string
                        minLength: 1
                        maxLength: 20
                        pattern: (\s*[^\s]\s*)+
                        description: Número de la factura rectificada o sustituida.
                      fecha_expedicion:
                        type: string
                        pattern: \d{2,2}-\d{2,2}-\d{4,4}
                        description: Fecha de emisión de la factura rectificada o sustituida.
                no_sujeta:
                  description: Indica si la factura es no sujeta.
                  type: object
                  properties:
                    causa:
                      type: string
                      description: |
                        Causa de la no sujeción. Los posibles valores son:
                        <ol style="list-style: disc;">
                          <li>OT: No sujeto por el artículo 7 de la Norma Foral de IVA Otros supuestos de no sujeción</li>
                          <li>RL: No sujeto por reglas de localización</li>
                          <li>VT: No sujeto, ventas realizadas por cuenta de terceros (importe no computable a efectos de IVA ni de IRPF)</li>
                          <li>IE: No sujeto en el TAI por reglas de localización, pero repercute impuesto extranjero, IPS/IGIC o IVA de otro estado miembro UE</li>
                        </ol>
                    importe:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Importe en euros correspondiente a la operación no sujeta.
                exenta:
                  description: Indica si la factura es exenta.
                  type: object
                  properties:
                    causa_exencion:
                      type: string
                      description: |
                        Causa de la exención. Los posibles valores son:
                        <ol style="list-style: disc;">
                          <li>E1: Exenta por el artículo 20 de la Norma Foral del IVA</li>
                          <li>E2: Exenta por el artículo 21 de la Norma Foral del IVA</li>
                          <li>E3: Exenta por el artículo 22 de la Norma Foral del IVA</li>
                          <li>E4: Exenta por los artículos 23 y 24 de la Norma Foral del IVA</li>
                          <li>E5: Exenta por el artículo 25 de la Norma Foral del IVA</li>
                          <li>E6: Exenta por otra causa</li>
                        </ol>
                    base_imponible:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Base imponible exenta en euros correspondiente a la causa de exención.
                especial:
                  type: object
                  description: Aquí se recogen campos opcionales adicionales.
                  properties:
                    inversion_sujeto_pasivo:
                      type: boolean
                      default: false
                      description: Con inversión del sujeto pasivo.
                    retencion_soportada:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Retención soportada.
                    base_imponible_a_coste:
                      type: string
                      pattern: (\+|-)?\d{1,12}(\.\d{0,2})?
                      description: Base imponible a coste (para grupos de IVA – nivel avanzado).
                    clave_regimen:
                      type: string
                      default: "01"
                      enum:
                        - "01"
                        - "02"
                        - "03"
                        - "04"
                        - "05"
                        - "06"
                        - "07"
                        - "08"
                        - "09"
                        - "10"
                        - "11"
                        - "12"
                        - "13"
                        - "14"
                        - "15"
                        - "17"
                        - "19"
                        - "51"
                        - "52"
                        - "53"
                        - "54"
                      description: Clave de régimen especial de IVA y operaciones con trascendencia tributaria.
              example:
                accion: SUBSANAR
                serie: A
                numero: "234634"
                fecha_expedicion: CURRENT_DATE
                simplificada: false
                nif: A15022510
                nombre: Empresa de ejemplo SL
                direccion: direccion cliente
                cp: "48992"
                descripcion: descripcion de la factura
                tipo_operacion: servicios
                lineas:
                  - descripcion: linea 1
                    cantidad: "2"
                    importe_unitario: "100.00"
                    importe_total: "242.00"
                desglose_iva:
                  - base_imponible: "200.00"
                    tipo_impositivo: "21"
                    cuota_impuesto: "42"
                importe_total: "242.00"
      responses:
        "200":
          description: Factura modificada
          content:
            application/json:
              schema:
                type: object
                properties:
                  uuid:
                    description: Identificador único del registro para poder consultar su estado.
                    type: string
                    example: ea6eae92-bac6-48c3-baa4-2bbf6b5ee562
                  tbai:
                    description: Código TicketBai.
                    type: string
                    example: TBAI-B05434202-021224-mwUG7NRo2ZNxp-010
                  estado:
                    description: Estado del registro. Siempre será igual a "Pendiente" como respuesta a una creación. Dicho estado se puede consultar posteriormente con el endpoint `/status`.
                    type: string
                    example: Pendiente
                  url:
                    description: URL de verificación del código QR.
                    type: string
                    example: https://pruebas-ticketbai.araba.eus/tbai/qrtbai/?id=TBAI-B05434202-021224-mwUG7NRo2ZNxp-010&s=A&nf=234634&i=242&cr=010
                  qr:
                    description: Código QR en base 64 que contiene la url.
                    type: string
                    example: jBBWRw0KGgoABAANSUhEAH0CAIAAABE...
        "500":
          description: Error de servidor
  /ticketbai/cancel:
    post:
      summary: Anular factura
      description: |
        Este endpoint permite anular facturas existentes. Al hacerlo, se envia la nueva información de la misma a hacienda. Una vez una factura está anulada, no se
        puede crear una nueva con el mismo (`serie`, `numero`).

        &nbsp;

        Este endpoint admite además una cabecera opcional `Idempotency-Key`. Si se incluye, los reintentos de la misma petición lógica con la misma clave
        replican la respuesta original (incluyendo la cabecera de respuesta `Idempotent-Replayed: true` y el
        eco de `Idempotency-Key`). Las claves se vinculan al NIF y se recuerdan durante 24 horas.
        Reusar la misma clave con un cuerpo diferente devuelve 422; un reintento concurrente mientras el
        primero sigue en curso devuelve 409.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request POST 'https://api.verifacti.com/ticketbai/cancel' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data-raw '{
              "serie": "A",
              "numero": "234634",
              "fecha_expedicion": "CURRENT_DATE"
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/cancel'
            headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            }
            payload = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE'
            }
            response = requests.post(url, headers=headers, json=payload)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/cancel';
            const headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            };
            const data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE'
            };
            fetch(url, { headers, method: 'POST', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'serie': 'A',
              'numero': '234634',
              'fecha_expedicion': 'CURRENT_DATE'
            };

            const response = await axios.post("https://api.verifacti.com/ticketbai/cancel", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/cancel";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
              "serie": "A",
              "numero": "234634",
              "fecha_expedicion": "CURRENT_DATE"
            }
            JSON;

            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n  \"serie\": \"A\",\n  \"numero\": \"234634\",\n  \"fecha_expedicion\": \"CURRENT_DATE\"\n}\n`\n\n\treq, err := http.NewRequest(\"POST\", \"https://api.verifacti.com/ticketbai/cancel\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                              "serie": "A",
                              "numero": "234634",
                              "fecha_expedicion": "CURRENT_DATE"
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/cancel"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
              ""serie"": ""A"",
              ""numero"": ""234634"",
              ""fecha_expedicion"": ""CURRENT_DATE""
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PostAsync("https://api.verifacti.com/ticketbai/cancel", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/cancel"

            oHttp.Open "POST", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "  ""serie"": ""A""," & vbCrLf & _
                   "  ""numero"": ""234634""," & vbCrLf & _
                   "  ""fecha_expedicion"": ""CURRENT_DATE""" & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          description: Clave única opcional para garantizar que reintentos de la misma petición no creen duplicados. Cadena ASCII imprimible de 1 a 255 caracteres. Se recuerda durante 24 horas por NIF.
          schema:
            type: string
            pattern: '^[\x20-\x7E]{1,255}$'
            minLength: 1
            maxLength: 255
            example: 8e1b9c2a-3f4d-4a6b-9c2e-1d2f3e4a5b6c
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - serie
                - numero
                - fecha_expedicion
              properties:
                serie:
                  type: string
                  maxLength: 20
                  description: Serie de la factura.
                numero:
                  type: string
                  minLength: 1
                  maxLength: 20
                  pattern: (\s*[^\s]\s*)+
                  description: Número de la factura.
                fecha_expedicion:
                  type: string
                  pattern: \d{2,2}-\d{2,2}-\d{4,4}
                  description: Fecha de emisión de la factura. No puede ser una fecha posterior a la actual.
                rechazo_previo:
                  type: boolean
                  default: false
                  description: |
                    Parámetro únicamente disponible en Álava y Guipúzcoa que indica si la factura que se quiere anular fue rechazada previamente.
                    Forma parte de lo que se conoce como el servicio ZUZENDU.
              example:
                serie: A
                numero: "234634"
                fecha_expedicion: CURRENT_DATE
      responses:
        "200":
          description: Factura anulada
          content:
            application/json:
              schema:
                type: object
                properties:
                  uuid:
                    description: Identificador único del registro para poder consultar su estado.
                    type: string
                    example: b018ced3-b362-4494-8776-9eefff1c160c
                  estado:
                    description: Estado del registro.
                    type: string
                    example: Pendiente
              example:
                estado: Pendiente
                uuid: b018ced3-b362-4494-8776-9eefff1c160c
        "500":
          description: Error de servidor
  /ticketbai/list:
    post:
      summary: Listar facturas
      description: |
        Las Haciendas del País Vasco no han habilitado un servicio de consulta de facturas via API, por lo tanto,
        mediante este endpoint se pueden consultar las facturas presentadas en la hacienda correspondiente tal y como se encuentran en nuestro sistema.
        Se devuelve un registro único por factura (`serie`, `numero` y `fecha_expedicion`) con el estado último de la misma en nuestro sistema.

        &nbsp;

        Para obtener un listado completo de las facturas emitidas, se pueden visitar las siguientes direcciones de la Hacienda correspondiente:

        <ul style="list-style: disc; margin-top: 6px;">
          <li><a href="https://ticketbai.araba.eus/tbai/consultafacturas/?idioma=es">Álava</a></li>
          <li><a href="https://www.gipuzkoa.eus/es/web/ogasuna/ticketbai/consulta-facturas">Guipuzcoa</a></li>
          <li><a href="https://www.batuz.eus/es/lroe">Vizcaya</a></li>
        </ul>

        En el entorno de test, los datos de las facturas se guardarán un máximo de 90 días.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request POST 'https://api.verifacti.com/ticketbai/list' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data-raw '{
              "rango_fecha_expedicion": {
                "desde": "03-12-2024",
                "hasta": "25-12-2025"
              },
              "serie": "A"
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/list'
            headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            }
            payload = {
              'rango_fecha_expedicion': {
                'desde': '03-12-2024',
                'hasta': '25-12-2025'
              },
              'serie': 'A'
            }
            response = requests.post(url, headers=headers, json=payload)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/list';
            const headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            };
            const data = {
              'rango_fecha_expedicion': {
                'desde': '03-12-2024',
                'hasta': '25-12-2025'
              },
              'serie': 'A'
            };
            fetch(url, { headers, method: 'POST', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'rango_fecha_expedicion': {
                'desde': '03-12-2024',
                'hasta': '25-12-2025'
              },
              'serie': 'A'
            };

            const response = await axios.post("https://api.verifacti.com/ticketbai/list", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/list";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
              "rango_fecha_expedicion": {
                "desde": "03-12-2024",
                "hasta": "25-12-2025"
              },
              "serie": "A"
            }
            JSON;

            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n  \"rango_fecha_expedicion\": {\n    \"desde\": \"03-12-2024\",\n    \"hasta\": \"25-12-2025\"\n  },\n  \"serie\": \"A\"\n}\n`\n\n\treq, err := http.NewRequest(\"POST\", \"https://api.verifacti.com/ticketbai/list\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                              "rango_fecha_expedicion": {
                                "desde": "03-12-2024",
                                "hasta": "25-12-2025"
                              },
                              "serie": "A"
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/list"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
              ""rango_fecha_expedicion"": {
                ""desde"": ""03-12-2024"",
                ""hasta"": ""25-12-2025""
              },
              ""serie"": ""A""
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PostAsync("https://api.verifacti.com/ticketbai/list", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/list"

            oHttp.Open "POST", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "  ""rango_fecha_expedicion"": {" & vbCrLf & _
                   "    ""desde"": ""03-12-2024""," & vbCrLf & _
                   "    ""hasta"": ""25-12-2025""" & vbCrLf & _
                   "  }," & vbCrLf & _
                   "  ""serie"": ""A""" & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      requestBody:
        content:
          application/json:
            schema:
              type: object
              example:
                rango_fecha_expedicion:
                  desde: 03-12-2024
                  hasta: 05-01-2025
                serie: A
              required:
                - rango_fecha_expedicion
              properties:
                rango_fecha_expedicion:
                  type: object
                  description: |
                    Rango de fechas de expedición de las facturas que se desean consultar.
                  properties:
                    desde:
                      type: string
                      pattern: \d{2,2}-\d{2,2}-\d{4,4}
                      description: Fecha de inicio del rango de fechas de expedición de las facturas que se desean consultar.
                    hasta:
                      type: string
                      pattern: \d{2,2}-\d{2,2}-\d{4,4}
                      description: |
                        Fecha de fin del rango de fechas de expedición de las facturas que se desean consultar.
                        Debe ser necesariamente mayor que `desde`.
                serie:
                  type: string
                  maxLength: 20
                  description: Serie de la factura.
                pagina:
                  type: integer
                  description: Este endpoint devuelve un máximo de 200 facturas por página.
                  default: 1
      responses:
        "200":
          description: Listado facturas
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    nif:
                      description: NIF del emisor de la factura.
                      type: string
                      example: A15022510
                    serie:
                      description: Serie de la factura.
                      type: string
                      example: A
                    numero:
                      description: Número de la factura.
                      type: string
                      example: 234634
                    fecha_expedicion:
                      description: Fecha de expedición de la factura.
                      type: string
                      example: CURRENT_DATE
                    estado:
                      description: |
                        Estado de factura:
                        <ol style="list-style: disc;">
                          <li>Pendiente: Registro encolado y no procesado aún.</li>
                          <li>Correcto: Registro procesado correctamente por la hacienda correspondiente.</li>
                          <li>Aceptado con errores: Registro aceptado con errores.</li>
                          <li>Incorrecto: Registro considerado incorrecto.</li>
                          <li>Anulada: Registro de anulación procesado correctamente por la hacienda correspondiente.</li>
                        </ol>
                      type: string
                    json_req:
                      description: JSON enviado a nuestra API.
                    url:
                      description: URL de verificación del código QR.
                      type: string
                    tbai:
                      description: Código TicketBai.
                      type: string
                    mensaje_error:
                      description: Descripcion del error tal y como aparece la hacienda correspondiente.
                      type: string
                  example:
                    nif: A15022510
                    estado: Correcto
                    json_req: "{\"serie\": \"A\", \"numero\": \"234634\", \"fecha_expedicion\": \"CURRENT_DATE\", ... }"
                    url: https://pruebas-ticketbai.araba.eus/tbai/qrtbai/?id=TBAI-A15022510-021224-mwUG7NRo2ZNxp-010&s=A&nf=34547&i=242&cr=010
                    tbai: TBAI-B05434202-021224-mwUG7NRo2ZNxp-010
  /ticketbai/export:
    post:
      summary: Exportar XMLs
      description: |
        Este endpoint permite exportar los ficheros XML en lotes, tanto de petición como de respuesta, de las facturas presentadas a Hacienda.

        &nbsp;

        La manera de descargar los ficheros es especificando el ejercicio y el periodo en que se generaron dichos registros.
        La respuesta de nuestra API consiste en una lista de URLs que contienen los ficheros XML de las facturas. Dichas URLs tienen una
        caducidad de 15 minutos. Cada llamada retorna un máximo de 500 URLs y, en caso de que existan más, se devuelve un token de paginación.
        Este token debe ser utilizado en la siguiente llamada para obtener las URLs restantes.

        &nbsp;

        En el entorno de test, los XMLs se guardarán un máximo de 90 días.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request POST 'https://api.verifacti.com/ticketbai/export' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data-raw '{
              "ejercicio": '2024',
              "periodo": '12',
              "token": "CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...",
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/export'
            headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            }
            payload = {
              'ejercicio': '2024',
              'periodo': '12',
              'token': 'CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...'
            }
            response = requests.post(url, headers=headers, json=payload)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/export';
            const headers = {
              'Authorization': 'Bearer <API_KEY>',
              'Content-Type': 'application/json'
            };
            const data = {
              'ejercicio': 2024,
              'periodo': 12,
              'token': 'CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...'
            };
            fetch(url, { headers, method: 'POST', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'ejercicio': 2024,
              'periodo': 12,
              'token': 'CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...'
            };

            const response = await axios.post("https://api.verifacti.com/ticketbai/export", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/export";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
              "ejercicio": '2024',
              "periodo": '12',
              "token": "CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...",
            }
            JSON;

            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n  \"ejercicio\": '2024',\n  \"periodo\": '12',\n  \"token\": \"CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...\",\n}\n`\n\n\treq, err := http.NewRequest(\"POST\", \"https://api.verifacti.com/ticketbai/export\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                              "ejercicio": '2024',
                              "periodo": '12',
                              "token": "CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...",
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/export"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
              ""ejercicio"": '2024',
              ""periodo"": '12',
              ""token"": ""CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD..."",
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PostAsync("https://api.verifacti.com/ticketbai/export", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/export"

            oHttp.Open "POST", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "  ""ejercicio"": '2024'," & vbCrLf & _
                   "  ""periodo"": '12'," & vbCrLf & _
                   "  ""token"": ""CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...""," & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      requestBody:
        content:
          application/json:
            schema:
              type: object
              example:
                ejercicio: "2024"
                periodo: "12"
                token: CkJ0ZXN0L0I3NTc3Nzg0Ny8yMD...
              required:
                - ejercicio
                - periodo
              properties:
                ejercicio:
                  type: string
                  description: Ejercicio de la fecha en que se generó el registro de facturación. No tiene por qué coincidir con la fecha de expedición y ni de operación.
                periodo:
                  type: string
                  description: Período de la fecha en que se generó el registro de facturación. No tiene por qué coincidir con la fecha de expedición y ni de operación.
                token:
                  type: string
                  description: Token de paginación.
      responses:
        "200":
          description: Listado de ficheros XML.
          content:
            application/json:
              schema:
                type: object
                properties:
                  urls:
                    type: array
                    description: |
                      Array con las URLs de los ficheros XML de las facturas. Estas URLs tienen una caducidad de 15 minutos.
                    maxItems: 500
                    items:
                      type: string
                      format: uri
                      description: URL de los ficheros XML de las facturas.
                    example:
                      - https://bucket/url_1_req.xml
                      - https://bucket/url_1_res.xml
                      - ...
                  token:
                    type: string
                    description: Token de paginación para obtener la página siguiente.
                    example: BkJ1ZXH0L5I3HTc7Nzg0Ny8ynH...
  /ticketbai/downloadXML:
    post:
      summary: Descargar XML
      description: |
        Mediante este endpoint se pueden descargar los ficheros XML de las facturas presentadas en TicketBai.
        &nbsp;

        La manera de acceder a los ficheros XML es mediante la serie y el número de la factura. Debido a que es posible que
        para una misma serie y número existan varios registros diferentes (de alta y anulación, por ejemplo), la respuesta
        es, en general, un array de ficheros XML como se puede ver en el ejemplo.

        &nbsp;

        En el entorno de test, los XMLs se guardarán un máximo de 90 días.
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: |
            curl --request POST 'https://api.verifacti.com/ticketbai/downloadXML' \
            --header 'Authorization: Bearer <API_KEY>' \
            --header 'Content-Type: application/json' \
            --data '{
                "serie": "A",
                "numero": "234634"
            }'
        - lang: Python
          label: Python
          source: |
            import requests
            url = 'https://api.verifacti.com/ticketbai/downloadXML'
            headers = {
              'Authorization': 'Bearer <API_KEY>'
            }
            data = {
              'serie': 'A',
              'numero': '234634'
            }
            response = requests.post(url, headers=headers, json=data)
        - lang: JavaScript
          label: JavaScript
          source: |
            const url = 'https://api.verifacti.com/ticketbai/downloadXML';
            const headers = {
              'Authorization': 'Bearer <API_KEY>'
            };
            const data = {
              'serie': 'A',
              'numero': '234634'
            };
            fetch(url, { headers, method: 'POST', body: JSON.stringify(data) })
              .then(response => response.json())
              .then(data => console.log(data))
              .catch(error => console.error(error));
        - lang: Node.js
          label: Node.js
          source: |-
            const axios = require("axios");

            const data = {
              'serie': 'A',
              'numero': '234634'
            };

            const response = await axios.post("https://api.verifacti.com/ticketbai/downloadXML", data, {
              headers: {
                "Authorization": "Bearer <API_KEY>",
                "Content-Type": "application/json"
              }
            });

            console.log(response.data);
        - lang: PHP
          label: PHP
          source: |-
            <?php

            $url = "https://api.verifacti.com/ticketbai/downloadXML";

            $headers = [
                "Authorization: Bearer <API_KEY>",
                "Content-Type: application/json"
            ];

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

            $body = <<<'JSON'
            {
                "serie": "A",
                "numero": "234634"
            }
            JSON;

            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            echo "HTTP Status: " . $httpCode . "\n";
            echo $response;
        - lang: Go
          label: Go
          source: "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc main() {\n\tbody := `\n{\n    \"serie\": \"A\",\n    \"numero\": \"234634\"\n}\n`\n\n\treq, err := http.NewRequest(\"POST\", \"https://api.verifacti.com/ticketbai/downloadXML\", strings.NewReader(body))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treq.Header.Set(\"Authorization\", \"Bearer <API_KEY>\")\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer resp.Body.Close()\n\n\trespBody, _ := io.ReadAll(resp.Body)\n\tfmt.Println(\"Status:\", resp.StatusCode)\n\tfmt.Println(string(respBody))\n}"
        - lang: Java
          label: Java
          source: |-
            import java.net.URI;
            import java.net.http.HttpClient;
            import java.net.http.HttpRequest;
            import java.net.http.HttpResponse;

            public class Main {
                public static void main(String[] args) throws Exception {
                    HttpClient client = HttpClient.newHttpClient();

                    String body = """
                            {
                                "serie": "A",
                                "numero": "234634"
                            }
                            """;

                    HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create("https://api.verifacti.com/ticketbai/downloadXML"))
                        .header("Authorization", "Bearer <API_KEY>")
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

                    HttpResponse<String> response = client.send(request,
                        HttpResponse.BodyHandlers.ofString());

                    System.out.println(response.statusCode());
                    System.out.println(response.body());
                }
            }
        - lang: C#
          label: C#
          source: |-
            using System;
            using System.Net.Http;
            using System.Text;
            using System.Threading.Tasks;

            class Program
            {
                static async Task Main()
                {
                    using var client = new HttpClient();
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer <API_KEY>");

                    var body = @"
                        {
                ""serie"": ""A"",
                ""numero"": ""234634""
            }
                    ";

                    var content = new StringContent(body, Encoding.UTF8, "application/json");

                    var response = await client.PostAsync("https://api.verifacti.com/ticketbai/downloadXML", content);

                    var responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine($"Status: {response.StatusCode}");
                    Console.WriteLine(responseBody);
                }
            }
        - lang: VB6
          label: VB6
          source: |-
            Dim oHttp As Object
            Dim sUrl As String
            Dim sResponse As String

            Set oHttp = CreateObject("MSXML2.XMLHTTP.6.0")

            sUrl = "https://api.verifacti.com/ticketbai/downloadXML"

            oHttp.Open "POST", sUrl, False
            oHttp.setRequestHeader "Authorization", "Bearer <API_KEY>"
            oHttp.setRequestHeader "Content-Type", "application/json"

            Dim sBody As String
            sBody = "{" & vbCrLf & _
                   "    ""serie"": ""A""," & vbCrLf & _
                   "    ""numero"": ""234634""" & vbCrLf & _
                   "}"

            oHttp.send sBody

            sResponse = oHttp.responseText
            Debug.Print "Status: " & oHttp.Status
            Debug.Print sResponse

            Set oHttp = Nothing
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - serie
                - numero
              properties:
                serie:
                  type: string
                  example: A
                  maxLength: 20
                  description: Serie de la factura.
                numero:
                  type: string
                  minLength: 1
                  maxLength: 20
                  pattern: (\s*[^\s]\s*)+
                  example: "234634"
                  description: Número de la factura.
      responses:
        "200":
          description: Ficheros XML
          content:
            application/json:
              schema:
                type: array
                items:
                  properties:
                    uuid:
                      description: Identificador único del registro.
                      type: string
                    operacion:
                      description: Tipo de operación.
                      type: string
                    xml_req:
                      description: Fichero XML de petición enviado a la hacienda correspondiente.
                      type: string
                    xml_res:
                      description: Fichero XML de respuesta de la hacienda correspondiente.
                      type: string
                example:
                  - uuid: b018ced3-b362-4494-8776-9eefff1c160c
                    operacion: Alta
                    xml_req: <?xml version="1.0" encoding="UTF-8"?><T:TicketBai ...
                    xml_res: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:TicketBaiResponse ...
          example: null
        "500":
          description: Error de servidor
components:
  schemas:
    estadoFactura:
      type: object
      properties:
        nif:
          description: NIF del emisor de la factura.
          type: string
          example: A15022510
        estado:
          description: |
            Estado de factura:
            <ol style="list-style: disc;">
              <li>Pendiente: Registro encolado y no procesado aún.</li>
              <li>Correcto: Registro procesado correctamente por la hacienda correspondiente.</li>
              <li>Aceptado con errores: Registro aceptado con errores.</li>
              <li>Incorrecto: Registro considerado incorrecto.</li>
              <li>Anulada: Registro de anulación procesado correctamente por la hacienda correspondiente.</li>
            </ol>
          type: string
        json_req:
          description: JSON enviado a nuestra API.
        url:
          description: URL de verificación del código QR.
          type: string
        tbai:
          description: Código TicketBai.
          type: string
        mensaje_error:
          description: Descripcion del error tal y como aparece la hacienda correspondiente.
          type: string
      example:
        nif: A15022510
        estado: Correcto
        json_req: "{\"serie\": \"A\", \"numero\": \"234634\", \"fecha_expedicion\": \"CURRENT_DATE\", ... }"
        url: https://pruebas-ticketbai.araba.eus/tbai/qrtbai/?id=TBAI-A15022510-021224-mwUG7NRo2ZNxp-010&s=A&nf=34547&i=242&cr=010
        tbai: TBAI-B05434202-021224-mwUG7NRo2ZNxp-010
