Process Receipts

Sync Processing

When using sync processing, request responds only after the receipt is processed, This method is suitable for scenarios where immediate feedback is required.

Submitting a job:

import axios from "axios";
// You can use
// - readFile if you're on Node.js
// - File input element if you're on browser
const file = ...;
const form = new FormData();
form.append("image", file);
// Optional; Tab-separataed list of category hints
// form.append("categories", ["food", "grocery"].join("\t"));
const options = {
  method: "POST",
  url: "https://eny.gege.mn/api/v1/receipts",
  headers: {
    "x-api-key": "your_api_key", // starts with 'eny'
  },
  data: form,
};
try {
  const result = await axios.request(options);
  console.log(result.data);
} catch (error) {
  console.error(error);
}
file.js

Example Response:

{
  "id": "6951633d3cb8e19dac7023e8",
  "status": "completed",
  "imageSHA256": "f1c47660d7ed1b2852da631fe68dc9f43f088b537b67053819143be58ff8932f",
  "imageUrl": "https://eny.gege.mn/api/v1/receipts/6951633d3cb8e19dac7023e8/image",
  "result": {
    "data": {
      "items": [
        {
          "name": "FILLET BURGER BOX",
          "amount": 16000,
          "currency": "MNT",
          "quantity": 1,
          "category": "Food"
        },
        {
          "name": "PACKAGE +500",
          "amount": 500,
          "currency": "MNT",
          "quantity": 1,
          "category": "Food"
        }
      ],
      "tax": 1500,
      "total": 16500,
      "currency": "MNT",
      "merchant": "KFC",
      "date": "2025-12-28T22:15:54.000",
      "paymentMethod": "card",
      "receiptNumber": "000412"
    },
    "error": null
  },
  "createdAt": "2025-12-28T17:05:01.165Z",
  "updatedAt": "2025-12-28T17:05:17.187Z",
  "completedAt": "2025-12-28T17:05:17.185Z"
}
GET /v1/receipts/:id

Async Processing

For async processing, the request returns immediately with a receipt ID. You can use this ID to check the processing status later.

Submitting an async job:

import axios from "axios";
// You can use
// - readFile if you're on Node.js
// - File input element if you're on browser
const file = ...;
const form = new FormData();
form.append("image", file);
// Optional; Tab-separataed list of category hints
// form.append("categories", ["food", "grocery"].join("\t"));
const options = {
  method: "POST",
  url: "https://eny.gege.mn/api/v1/receipts",
  params: { async: "" }, // remove async if you want synchronous processing
  headers: {
    "x-api-key": "your_api_key", // starts with 'eny'
  },
  data: form,
};
try {
  const result = await axios.request(options);
  console.log(result.data);
} catch (error) {
  console.error(error);
}
file.js

Example Response:

{
  "id": "6951633d3cb8e19dac7023e8",
  "status": "processing",
  "imageSHA256": "f1c47660d7ed1b2852da631fe68dc9f43f088b537b67053819143be58ff8932f",
  "createdAt": "2025-12-28T17:05:01.165Z",
  "updatedAt": "2025-12-28T17:05:01.165Z"
}
POST /v1/receipts?async

Retrieving job

import axios from "axios";
const id = "your_receipt_id"; // e.g., "41870e9a517e4e73b93b9a254204a49d"
const options = {
  method: "GET",
  url: `https://eny.gege.mn/api/v1/receipts/${id}`,
  headers: {
    "x-api-key": "your_api_key", // starts with 'eny'
  }
};
try {
  const result = await axios.request(options);
  console.log(result.data);
} catch (error) {
  console.error(error);
}
file.js

Example Response:

{
  "id": "6951633d3cb8e19dac7023e8",
  "status": "completed",
  "imageSHA256": "f1c47660d7ed1b2852da631fe68dc9f43f088b537b67053819143be58ff8932f",
  "imageUrl": "https://eny.gege.mn/api/v1/receipts/6951633d3cb8e19dac7023e8/image",
  "result": {
    "data": {
      "items": [
        {
          "name": "FILLET BURGER BOX",
          "amount": 16000,
          "currency": "MNT",
          "quantity": 1,
          "category": "Food"
        },
        {
          "name": "PACKAGE +500",
          "amount": 500,
          "currency": "MNT",
          "quantity": 1,
          "category": "Food"
        }
      ],
      "tax": 1500,
      "total": 16500,
      "currency": "MNT",
      "merchant": "KFC",
      "date": "2025-12-28T22:15:54.000",
      "paymentMethod": "card",
      "receiptNumber": "000412"
    },
    "error": null
  },
  "createdAt": "2025-12-28T17:05:01.165Z",
  "updatedAt": "2025-12-28T17:05:17.187Z",
  "completedAt": "2025-12-28T17:05:17.185Z"
}
GET /v1/receipts/:id

Receipt fields

A completed receipt has its data under result.data. Only items and total are always present; every other field appears only when it is printed on the receipt.

  • items: purchased items, each with name, amount (line total), currency, quantity, unitPrice and category
  • subtotal: sum before tax, tip and receipt-level discounts
  • discount: receipt-level discounts, as a positive number
  • tax: total tax amount (VAT, GST, sales tax…)
  • tip: tip, gratuity or service charge
  • total: final amount paid, after discounts and including tax and tip
  • currency: ISO 4217 currency code
  • merchant, address, tel, website, email: merchant details
  • category: category of the purchase as a whole
  • date: ISO 8601 date in the receipt’s local time, without a timezone
  • paymentMethod: one of cash, card, mobile, transfer, voucher, other
  • receiptNumber: receipt, invoice or order number

To get categories, send your own list as a tab-separated categories form field with the upload, or save a list in the dashboard. Categories are always picked from that list.

The full JSON Schema is available at /api/schema/25001.

Failed receipts

When a receipt cannot be processed, its status is failed, result.data is null and result.error.message explains why. An image that is not a receipt fails with “The image does not appear to be a receipt”. A failed receipt does not use a credit.

Long polling

Long polling allows you to wait for the server to provide the processing result.

You can combine this with async processing by making your initial request in a non-blocking manner. Max waiting period is 30 seconds, and you can reconnect if the connection drops.

The response is never cached, so you can call it again right away if the job has not finished yet.

import axios from "axios";
const id = "your_receipt_id"; // e.g., "41870e9a517e4e73b93b9a254204a49d"
const options = {
  method: "GET",
  url: `https://eny.gege.mn/api/v1/receipts/${id}`,
  headers: {
    "x-api-key": "your_api_key", // starts with 'eny'
  }
};
try {
  const result = await axios.request(options);
  console.log(result.data);
} catch (error) {
  console.error(error);
}
file.js

Example Response:

This endpoint only reports whether processing has finished, so fetch the processed receipt afterwards, i.e., GET /v1/receipts/:id

{
  "hasFinishedProcessing": true
}
GET /v1/receipts/:id/longpoll

This site uses Microsoft Clarity for analytics. See our Privacy Policy .