{
  "openapi": "3.1.0",
  "info": {
    "title": "Tabscanner API",
    "version": "2.0.0",
    "summary": "Upload a photo of a receipt, get structured JSON back.",
    "description": "Receipt and invoice OCR. Upload an image to `POST /v2/receipts`, then poll `GET /v2/receipts/{token}` until `status` is `done`. Errors use RFC 9457 problem details. Human-readable documentation: https://docs3.tabscanner.com.",
    "contact": {
      "name": "Tabscanner support",
      "email": "support@tabscanner.com",
      "url": "https://tabscanner.com"
    },
    "termsOfService": "https://tabscanner.com/terms",
    "license": {
      "name": "Proprietary",
      "url": "https://tabscanner.com/terms"
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key as a bearer token: `Authorization: Bearer <key>`."
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "apikey",
        "description": "Alternatively, your API key in an `apikey` header."
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "instance",
          "code",
          "requestId"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "URI identifying the error type, `urn:tabscanner:error:<code>`."
          },
          "title": {
            "type": "string",
            "description": "Short, human-readable summary of the error type."
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code, repeated for convenience."
          },
          "detail": {
            "type": "string",
            "description": "Explanation specific to this occurrence. Not always present."
          },
          "instance": {
            "type": "string",
            "description": "The request path."
          },
          "code": {
            "type": "string",
            "enum": [
              "FORM_PARSER",
              "IDEMPOTENCY_KEY_INVALID",
              "MODEL_UNKNOWN",
              "NO_FILE",
              "TOO_MANY_FILE_FIELDS",
              "TOO_MANY_FILES",
              "VALIDATION",
              "API_KEY_INVALID",
              "INSUFFICIENT_CREDIT",
              "SUBSCRIPTION_INACTIVE",
              "SUBSCRIPTION_NOT_FOUND",
              "MODEL_NOT_PERMITTED",
              "PDF_NOT_PERMITTED",
              "NOT_FOUND",
              "TOKEN_NOT_FOUND",
              "IDEMPOTENCY_IN_PROGRESS",
              "FILE_TOO_LARGE",
              "UNSUPPORTED_EXTENSION",
              "UNSUPPORTED_FILE_TYPE",
              "UNSUPPORTED_MIME_TYPE",
              "IMAGE_PROCESSING",
              "RATE_LIMITED",
              "DB_QUERY",
              "INTERNAL",
              "RESULT_INVALID",
              "DB_CONNECTION",
              "QUEUE",
              "SERVICE_UNAVAILABLE",
              "STORAGE"
            ],
            "description": "Stable, machine-readable error code. See the error reference."
          },
          "requestId": {
            "type": "string",
            "description": "Identifier of this request. Quote it when contacting support."
          }
        },
        "description": "RFC 9457 problem details, returned with `Content-Type: application/problem+json` for every error. Branch on `code`, which is stable; `title` and `detail` are for people.",
        "example": {
          "type": "urn:tabscanner:error:insufficient-credit",
          "title": "Not enough credit",
          "status": 402,
          "instance": "/v2/receipts",
          "code": "INSUFFICIENT_CREDIT",
          "requestId": "8e395308b68c2e6f88663446b8708121"
        }
      },
      "Result": {
        "type": "object",
        "description": "Structured data extracted from the receipt. Scalar fields are `null` when not found. Amounts are decimal\nnumbers in the receipt's currency. Confidence values range from 0 (no confidence) to 1 (certain).\n",
        "additionalProperties": true,
        "properties": {
          "establishment": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name of the merchant. Detected by machine learning and, where configured, custom establishment lookups."
          },
          "establishmentConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `establishment` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "validatedEstablishment": {
            "type": "boolean",
            "description": "`true` when the establishment was cross-referenced with the phone number or address on the receipt and confirmed in Tabscanner's database."
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "description": "Purchase date and time as `YYYY-MM-DD hh:mm:ss`. Time is `00:00:00` when not printed.",
            "examples": [
              "2026-03-14 12:41:00"
            ]
          },
          "dateISO": {
            "type": [
              "string",
              "null"
            ],
            "description": "Purchase date and time as ISO 8601 `YYYY-MM-DDThh:mm:ss`.",
            "examples": [
              "2026-03-14T12:41:00"
            ]
          },
          "dateConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `date` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "total": {
            "type": [
              "number",
              "null"
            ],
            "description": "Grand total paid."
          },
          "totalConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `total` is correct and is the total, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "validatedTotal": {
            "type": "boolean",
            "description": "`true` when `totalConfidence` is at least 0.99."
          },
          "subTotal": {
            "type": [
              "number",
              "null"
            ],
            "description": "Amount before tax, tip and service charges."
          },
          "subTotalConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `subTotal` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "validatedSubTotal": {
            "type": "boolean",
            "description": "`true` when `subTotalConfidence` is at least 0.99."
          },
          "tax": {
            "type": [
              "number",
              "null"
            ],
            "description": "Total tax amount."
          },
          "taxes": {
            "type": "array",
            "description": "Each tax amount found, in receipt order.",
            "items": {
              "type": "number"
            }
          },
          "taxesConfidence": {
            "type": "array",
            "description": "Confidence for each entry of `taxes`, same order.",
            "items": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          "tip": {
            "type": [
              "number",
              "null"
            ],
            "description": "Tip or gratuity amount."
          },
          "tipConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `tip` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "serviceCharges": {
            "type": "array",
            "description": "Each service charge amount found.",
            "items": {
              "type": "number"
            }
          },
          "serviceChargeConfidences": {
            "type": "array",
            "description": "Confidence for each entry of `serviceCharges`, same order.",
            "items": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          "discount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Total discount applied to the receipt."
          },
          "discounts": {
            "type": "array",
            "description": "Each discount amount found.",
            "items": {
              "type": "number"
            }
          },
          "discountConfidences": {
            "type": "array",
            "description": "Confidence for each entry of `discounts`, same order.",
            "items": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          "rounding": {
            "type": [
              "number",
              "null"
            ],
            "description": "Cash rounding applied to the total, if any."
          },
          "roundingConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `rounding` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "cash": {
            "type": [
              "number",
              "null"
            ],
            "description": "Cash tendered."
          },
          "cashConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `cash` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "change": {
            "type": [
              "number",
              "null"
            ],
            "description": "Change returned to the customer."
          },
          "changeConfidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence that `change` is correct, 0 to 1.",
            "minimum": 0,
            "maximum": 1
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Detected ISO 4217 currency code. Known values include USD, EUR, GBP, AED, CHF, AUD, HKD, JPY, KRW, RMB, BRL, CAD, ZAR."
          },
          "paymentMethod": {
            "type": [
              "string",
              "null"
            ],
            "description": "Payment method as printed. Known values include VISA, Mastercard, American Express, Discover, ALIPAY, WE CHAT, CASH, Debit."
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Merchant address text exactly as extracted, not normalised."
          },
          "addressNorm": {
            "$ref": "#/components/schemas/AddressNorm"
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Merchant phone number as printed, not normalised."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Website address printed on the receipt."
          },
          "barcodes": {
            "type": "array",
            "description": "Barcodes found on the receipt. Each entry is a two-element array of `[data, type]`. Types include EAN-13/UPC-A, UPC-E, EAN-8, Code 128, Code 39, Interleaved 2 of 5 and QR Code.",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "minItems": 2,
              "maxItems": 2
            }
          },
          "expenseType": {
            "type": [
              "string",
              "null"
            ],
            "description": "Beta. Expense classification, for example `Meals/Individual Meals while Traveling`, `Transportation-Rideshare/Uber/Lyft/Taxi`, `Travel Expenses/Hotel`."
          },
          "documentType": {
            "type": [
              "string",
              "null"
            ],
            "description": "The document type. The value sent as `documentType` (default `receipt`), or the detected type, `Receipt` or `Invoice`, when `documentType=auto` was sent."
          },
          "customFields": {
            "type": "object",
            "description": "Account-specific extracted fields, for example `Country`, `CardLast4Digits`, `ReceiptNumber`, `VATNumber`. Keys depend on your configuration.",
            "additionalProperties": true
          },
          "otherData": {
            "type": "array",
            "description": "Additional raw data. Only populated for accounts with this feature enabled (`features.otherData` on `GET /v2/account`); otherwise an empty array.",
            "items": {}
          },
          "lineItems": {
            "type": "array",
            "description": "Product lines found on the receipt, top to bottom.",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          },
          "summaryItems": {
            "type": "array",
            "description": "Non-product lines such as Total, Sub Total, Tax, Cash, Change, Tip and Service Charge, as `LineItem` objects with `lineType` set.",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            }
          }
        }
      },
      "AddressNorm": {
        "type": [
          "object",
          "null"
        ],
        "description": "The merchant address split into components. Any component may be `null`.",
        "properties": {
          "building": {
            "type": [
              "string",
              "null"
            ],
            "description": "Building name or unit."
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Street number."
          },
          "street": {
            "type": [
              "string",
              "null"
            ],
            "description": "Street name."
          },
          "suburb": {
            "type": [
              "string",
              "null"
            ],
            "description": "Suburb or district."
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "description": "City or town."
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "State, province or region."
          },
          "postcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Postal code."
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "Country."
          }
        }
      },
      "LineItem": {
        "type": "object",
        "description": "One line of the receipt, either a product (`lineItems`) or a summary line (`summaryItems`).",
        "additionalProperties": true,
        "properties": {
          "desc": {
            "type": [
              "string",
              "null"
            ],
            "description": "Text found on the same printed line as `lineTotal`."
          },
          "descClean": {
            "type": [
              "string",
              "null"
            ],
            "description": "Consolidated, cleaned description including text from adjacent lines that belong to this item, with prices and discounts removed."
          },
          "lineTotal": {
            "type": [
              "number",
              "null"
            ],
            "description": "Amount at the end of the line."
          },
          "price": {
            "type": [
              "number",
              "null"
            ],
            "description": "Unit price when printed separately from `lineTotal`."
          },
          "qty": {
            "type": [
              "number",
              "null"
            ],
            "description": "Quantity when printed. Defaults to `0` when no quantity is found; a value of `1` means a `1` was actually printed."
          },
          "unit": {
            "type": "string",
            "description": "Unit of measure for items sold by weight or volume, as printed, for example `kg`, `lbs` or `l`. Empty string when no unit is printed."
          },
          "productCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "SKU, PLU or barcode number found on the line."
          },
          "symbols": {
            "type": "array",
            "description": "Symbols printed after the amount, typically tax codes such as `A`, `B`, `*`.",
            "items": {
              "type": "string"
            }
          },
          "supplementaryLineItems": {
            "type": [
              "object",
              "null"
            ],
            "description": "Present only when text above or below the line could not be resolved into `descClean`. Contains the unresolved text.",
            "properties": {
              "above": {
                "type": "array",
                "description": "Unresolved lines printed above this item.",
                "items": {
                  "type": "string"
                }
              },
              "below": {
                "type": "array",
                "description": "Unresolved lines printed below this item.",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "discount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Discount applied to this line, when printed."
          },
          "customFields": {
            "type": "object",
            "description": "Account-specific fields extracted for this line. Empty unless configured for your account.",
            "additionalProperties": true
          },
          "lineType": {
            "type": [
              "string",
              "null"
            ],
            "description": "Classification of the line. For `summaryItems` one of `Total`, `SubTotal`, `Tax`, `TotalTax`, `Cash`, `Change`, `Tip`, `ServiceCharge`. For product lines this is `Product` or `null`."
          }
        }
      },
      "UploadForm": {
        "type": "object",
        "required": [
          "file"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "The receipt image, JPEG or PNG, up to 30 MB. Accounts with multi-image uploads enabled can send up to three parts named `file` for one long receipt photographed in sections; they are processed as a single document."
          },
          "region": {
            "type": "string",
            "enum": [
              "ar",
              "au",
              "be",
              "br",
              "ca",
              "cl",
              "co",
              "fr",
              "de",
              "gr",
              "hk",
              "in",
              "id",
              "ie",
              "it",
              "ja",
              "ke",
              "my",
              "mx",
              "nz",
              "pa",
              "pe",
              "ph",
              "sg",
              "za",
              "es",
              "se",
              "ch",
              "to",
              "ae",
              "gb",
              "uy",
              "us",
              "vn"
            ],
            "description": "Two-letter ISO 3166-1 country code of the receipt, lower case. Selects number and date formats, language and regional extraction rules. Strongly recommended."
          },
          "documentType": {
            "type": "string",
            "enum": [
              "receipt",
              "invoice",
              "auto"
            ],
            "default": "receipt",
            "description": "Type of document in the image. `auto` detects it and reports it in `result.documentType`."
          },
          "model": {
            "type": "string",
            "enum": [
              "harajuku",
              "harajuku-pro"
            ],
            "description": "Process with one of the Tabscanner models instead of standard OCR. Omit for standard OCR. Available to every account; the `-pro` models need the Pro package (`features.proModels` on `GET /v2/account`). See Models."
          },
          "decimalPlaces": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3,
            "description": "How many decimal places amounts are printed with (for example 3 for KWD or BHD). A parsing hint, not an output format."
          },
          "defaultDateParsing": {
            "type": "string",
            "enum": [
              "d/m",
              "m/d"
            ],
            "description": "How to read an ambiguous date such as `02/03/2026`: `d/m` reads 2 March, `m/d` reads 3 February."
          },
          "cents": {
            "type": "boolean",
            "default": false,
            "description": "Read amounts printed without a decimal separator as minor units. Only with `decimalPlaces=3` (`245` becomes `0.245`). Send `true` or `false`."
          }
        }
      },
      "UploadAccepted": {
        "description": "The upload was accepted and queued.",
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{16}$",
            "description": "Identifies the upload. Pass it to `GET /v2/receipts/{token}`."
          },
          "status": {
            "type": "string",
            "const": "queued",
            "description": "Always `queued`."
          },
          "duplicate": {
            "type": "boolean",
            "description": "`true` when a visually identical image was uploaded before on this account."
          },
          "duplicateToken": {
            "type": "string",
            "description": "Token of the earlier upload. Present only when `duplicate` is `true`."
          },
          "warnings": {
            "type": "array",
            "description": "Non-fatal issues with the upload. Currently `LOW_RESOLUTION`: an image is smaller than 720 by 1280 pixels.",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "detail": {
                  "type": "string"
                }
              },
              "required": [
                "code",
                "detail"
              ]
            }
          },
          "links": {
            "type": "object",
            "description": "Related URLs.",
            "properties": {
              "result": {
                "type": "string",
                "description": "Path of the result endpoint for this token."
              }
            }
          }
        }
      },
      "ReceiptResult": {
        "type": "object",
        "description": "The state of an upload and, once processed, its data.",
        "required": [
          "token",
          "status"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "The token from the upload."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "done",
              "failed"
            ],
            "description": "`pending`: still processing, call again after `Retry-After`. `done`: `result` is present. `failed`: the document could not be processed."
          },
          "result": {
            "$ref": "#/components/schemas/Result"
          },
          "error": {
            "type": "object",
            "description": "Present when `status` is `failed`.",
            "properties": {
              "code": {
                "type": "string",
                "const": "OCR_FAILED"
              },
              "detail": {
                "type": "string"
              }
            }
          }
        }
      },
      "Account": {
        "description": "The authenticated account.",
        "properties": {
          "credit": {
            "type": "number",
            "description": "Remaining credit. Each accepted upload uses one."
          },
          "billing": {
            "type": "string",
            "enum": [
              "credit",
              "subscription"
            ],
            "description": "`credit`: prepaid credit is decremented per upload. `subscription`: uploads are billed to a subscription."
          },
          "dataRetentionDays": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Days that uploads and results are kept before deletion."
          },
          "features": {
            "type": "object",
            "description": "Features enabled for this account.",
            "properties": {
              "multipleImages": {
                "type": "boolean",
                "description": "Up to three images may be sent in one upload."
              },
              "pdf": {
                "type": "boolean",
                "description": "PDF uploads are accepted. Currently always `false`."
              },
              "otherData": {
                "type": "boolean",
                "description": "`result.otherData` is populated."
              },
              "models": {
                "type": "boolean",
                "description": "The `model` upload field may be used. True for every account."
              },
              "proModels": {
                "type": "boolean",
                "description": "The `-pro` models may be used. True on paid plans with the Pro package."
              }
            }
          }
        }
      },
      "Region": {
        "type": "object",
        "description": "A supported region.",
        "properties": {
          "region": {
            "type": "string",
            "description": "Region code, to send as `region`."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name."
          }
        }
      },
      "CustomField": {
        "type": "object",
        "description": "A custom field that can be extracted in a region.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Key under `result.customFields`."
          },
          "domain": {
            "type": [
              "string",
              "null"
            ],
            "description": "Business domain the field applies to."
          },
          "region": {
            "type": "string",
            "description": "Region code the field is configured for."
          }
        }
      }
    }
  },
  "paths": {
    "/v2/receipts": {
      "post": {
        "summary": "Upload a receipt for processing",
        "tags": [
          "receipts"
        ],
        "description": "Queues one receipt for processing and returns at once with a token. Each accepted upload uses one credit; rejected uploads are not charged. Poll the URL in `Location`, waiting `Retry-After` seconds between calls.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UploadForm"
              },
              "encoding": {
                "file": {
                  "contentType": "image/jpeg, image/png"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Unique value for this upload, such as a UUID. Retrying with the same key within 24 hours returns the original response instead of uploading and charging again."
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted and queued.",
            "headers": {
              "Location": {
                "description": "Path of the result, `/v2/receipts/{token}`.",
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before the first poll.",
                "schema": {
                  "type": "integer"
                }
              },
              "Idempotent-Replayed": {
                "description": "`true` when this is the stored response to an earlier request with the same `Idempotency-Key`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Request-Id": {
                "description": "Identifier of this request. Also in error bodies as `requestId`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadAccepted"
                },
                "examples": {
                  "accepted": {
                    "value": {
                      "token": "TC72MdZUW69PpPy9",
                      "status": "queued",
                      "duplicate": false,
                      "warnings": [],
                      "links": {
                        "result": "/v2/receipts/TC72MdZUW69PpPy9"
                      }
                    }
                  },
                  "duplicate": {
                    "value": {
                      "token": "Yfh3vKH2dCHg9W2w",
                      "status": "queued",
                      "duplicate": true,
                      "duplicateToken": "TC72MdZUW69PpPy9",
                      "warnings": [],
                      "links": {
                        "result": "/v2/receipts/Yfh3vKH2dCHg9W2w"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`FORM_PARSER`, `IDEMPOTENCY_KEY_INVALID`, `MODEL_UNKNOWN`, `NO_FILE`, `TOO_MANY_FILE_FIELDS`, `TOO_MANY_FILES`, `VALIDATION`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "noFile": {
                    "value": {
                      "type": "urn:tabscanner:error:no-file",
                      "title": "No file detected",
                      "status": 400,
                      "instance": "/v2/receipts",
                      "code": "NO_FILE",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  },
                  "unknownModel": {
                    "value": {
                      "type": "urn:tabscanner:error:model-unknown",
                      "title": "Unknown model",
                      "status": 400,
                      "detail": "Unknown model \"shibya\". Valid models: harajuku, harajuku-pro",
                      "instance": "/v2/receipts",
                      "code": "MODEL_UNKNOWN",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "`API_KEY_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "apiKey": {
                    "value": {
                      "type": "urn:tabscanner:error:api-key-invalid",
                      "title": "Missing or invalid API key",
                      "status": 401,
                      "instance": "/v2/receipts",
                      "code": "API_KEY_INVALID",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "`INSUFFICIENT_CREDIT`, `SUBSCRIPTION_INACTIVE`, `SUBSCRIPTION_NOT_FOUND`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "noCredit": {
                    "value": {
                      "type": "urn:tabscanner:error:insufficient-credit",
                      "title": "Not enough credit",
                      "status": 402,
                      "instance": "/v2/receipts",
                      "code": "INSUFFICIENT_CREDIT",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "`MODEL_NOT_PERMITTED`, `PDF_NOT_PERMITTED`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "modelNotPermitted": {
                    "value": {
                      "type": "urn:tabscanner:error:model-not-permitted",
                      "title": "This model is not available on your plan",
                      "status": 403,
                      "detail": "Pro models require the Pro package. Use \"harajuku\" instead, or omit `model` for standard OCR.",
                      "instance": "/v2/receipts",
                      "code": "MODEL_NOT_PERMITTED",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`IDEMPOTENCY_IN_PROGRESS`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "`FILE_TOO_LARGE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "`UNSUPPORTED_EXTENSION`, `UNSUPPORTED_FILE_TYPE`, `UNSUPPORTED_MIME_TYPE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "unsupported": {
                    "value": {
                      "type": "urn:tabscanner:error:unsupported-mime-type",
                      "title": "Unsupported MIME type",
                      "status": 415,
                      "detail": "Unsupported MIME type image/gif",
                      "instance": "/v2/receipts",
                      "code": "UNSUPPORTED_MIME_TYPE",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IMAGE_PROCESSING`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`RATE_LIMITED`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "`DB_QUERY`, `INTERNAL`, `RESULT_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "`DB_CONNECTION`, `QUEUE`, `SERVICE_UNAVAILABLE`, `STORAGE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "uploadReceipt"
      }
    },
    "/v2/receipts/{token}": {
      "get": {
        "summary": "Get a receipt result",
        "tags": [
          "receipts"
        ],
        "description": "Short-poll this endpoint until `status` is `done` or `failed`. While pending, `Retry-After` gives the suggested poll interval in seconds.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{1,64}$"
            },
            "description": "The token from the upload."
          }
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "The current state. A pending result is not an error: keep polling.",
            "headers": {
              "Retry-After": {
                "description": "While `pending`: seconds to wait before polling again.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Request-Id": {
                "description": "Identifier of this request. Also in error bodies as `requestId`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptResult"
                },
                "examples": {
                  "pending": {
                    "value": {
                      "token": "TC72MdZUW69PpPy9",
                      "status": "pending"
                    }
                  },
                  "done": {
                    "value": {
                      "token": "TC72MdZUW69PpPy9",
                      "status": "done",
                      "result": {
                        "establishment": "GREENLEAF CAFE",
                        "establishmentConfidence": 0.8,
                        "validatedEstablishment": false,
                        "date": "2026-10-08 12:41:00",
                        "dateISO": "2026-10-08T12:41:00",
                        "dateConfidence": 0.95,
                        "total": 40.22,
                        "totalConfidence": 0.99,
                        "subTotal": 35.75,
                        "tax": 5.96,
                        "taxes": [
                          5.96
                        ],
                        "serviceCharges": [
                          4.47
                        ],
                        "currency": "GBP",
                        "paymentMethod": "VISA",
                        "address": "12 Market Street, London EC1A 1BB",
                        "lineItems": [
                          {
                            "qty": 2,
                            "desc": "2 x Flat White 7.00",
                            "descClean": "Flat White",
                            "lineTotal": 7,
                            "price": 3.5,
                            "lineType": "Product"
                          },
                          {
                            "qty": 1,
                            "desc": "1 x Avocado Toast 9.50",
                            "descClean": "Avocado Toast",
                            "lineTotal": 9.5,
                            "price": 9.5,
                            "lineType": "Product"
                          }
                        ],
                        "otherData": []
                      }
                    }
                  },
                  "failed": {
                    "value": {
                      "token": "TC72MdZUW69PpPy9",
                      "status": "failed",
                      "error": {
                        "code": "OCR_FAILED",
                        "detail": "The document could not be processed"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`FORM_PARSER`, `IDEMPOTENCY_KEY_INVALID`, `MODEL_UNKNOWN`, `NO_FILE`, `TOO_MANY_FILE_FIELDS`, `TOO_MANY_FILES`, `VALIDATION`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "`API_KEY_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`, `TOKEN_NOT_FOUND`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "notFound": {
                    "value": {
                      "type": "urn:tabscanner:error:token-not-found",
                      "title": "Token not found",
                      "status": 404,
                      "instance": "/v2/receipts",
                      "code": "TOKEN_NOT_FOUND",
                      "requestId": "8e395308b68c2e6f88663446b8708121"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "`RATE_LIMITED`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "`DB_QUERY`, `INTERNAL`, `RESULT_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "`DB_CONNECTION`, `QUEUE`, `SERVICE_UNAVAILABLE`, `STORAGE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getReceipt"
      }
    },
    "/v2/account": {
      "get": {
        "summary": "Get the authenticated account",
        "tags": [
          "account"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "The account.",
            "headers": {
              "X-Request-Id": {
                "description": "Identifier of this request. Also in error bodies as `requestId`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "example": {
                  "credit": 4821,
                  "billing": "subscription",
                  "dataRetentionDays": 90,
                  "features": {
                    "multipleImages": false,
                    "pdf": false,
                    "otherData": false,
                    "models": true,
                    "proModels": false
                  }
                }
              }
            }
          },
          "401": {
            "description": "`API_KEY_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`RATE_LIMITED`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "`DB_QUERY`, `INTERNAL`, `RESULT_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "`DB_CONNECTION`, `QUEUE`, `SERVICE_UNAVAILABLE`, `STORAGE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "getAccount",
        "description": "Free: does not use credit."
      }
    },
    "/v2/regions": {
      "get": {
        "summary": "List supported regions",
        "tags": [
          "reference"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "All regions.",
            "headers": {
              "X-Request-Id": {
                "description": "Identifier of this request. Also in error bodies as `requestId`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Region"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "region": "GB",
                      "name": "United Kingdom"
                    },
                    {
                      "region": "US",
                      "name": "United States"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`API_KEY_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`RATE_LIMITED`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "`DB_QUERY`, `INTERNAL`, `RESULT_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "`DB_CONNECTION`, `QUEUE`, `SERVICE_UNAVAILABLE`, `STORAGE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listRegions",
        "description": "The regions with dedicated configurations. Free."
      }
    },
    "/v2/custom-fields": {
      "get": {
        "summary": "List custom extraction fields by region",
        "tags": [
          "reference"
        ],
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "All custom fields.",
            "headers": {
              "X-Request-Id": {
                "description": "Identifier of this request. Also in error bodies as `requestId`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomField"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "name": "ReceiptNumber",
                      "domain": "retail",
                      "region": "GB"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "`API_KEY_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "`RATE_LIMITED`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "500": {
            "description": "`DB_QUERY`, `INTERNAL`, `RESULT_INVALID`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "`DB_CONNECTION`, `QUEUE`, `SERVICE_UNAVAILABLE`, `STORAGE`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "operationId": "listCustomFields",
        "description": "Custom fields configured per region. Free."
      }
    }
  },
  "tags": [
    {
      "name": "receipts",
      "description": "Upload receipts and collect results."
    },
    {
      "name": "account",
      "description": "Your account and credit."
    },
    {
      "name": "reference",
      "description": "Reference data for request options."
    }
  ],
  "externalDocs": {
    "description": "Documentation",
    "url": "https://docs3.tabscanner.com"
  },
  "servers": [
    {
      "url": "https://api3.tabscanner.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyHeader": []
    }
  ]
}
