Interfaces / API

Receipts Space offers several interfaces to create, import, process, and export documents and receipts programmatically.

Overview

Import

  • File system - drag & drop files or folders, open them via File > Open, or pass them as command-line arguments. Supported formats include PDF, images (JPEG, PNG, HEIC …), e-mails (EML), and JSON files in the format described below.
  • Folder watching - configured folders (e.g. a scanner output folder or an iCloud folder) are watched permanently; new files are imported automatically.
  • JSON import - structured import of complete documents, including asset, amounts, categories, contacts, and tags.
  • URL scheme - invocation from a browser, mail client, or Shortcuts via receipts-space://.... Ideal for importing directly into the library from web portals.

Export

  • Drag & drop / clipboard - selected documents can be handed over to other apps as PDF, as the original file, or in JSON format (the same schema as the JSON import).
  • File > Export - batch export into a folder, with optional metadata sidecars.
  • AppleScript export command - programmatic export, filtered by date, status, category, contact, or tag. GitHub holtwick/receipts-api

Automation

If you have any further questions, our support team is happy to help.

Viewing Log Files

When working with these interfaces, the log is often the quickest way to diagnose issues. In the app’s main menu, under Help:

  • Normal: the item reads Support & Feedback - opens the feedback form.
  • With the Option (alt) key held down: the item switches to Open Log Files and reveals the log folder in Finder.

Direct path:

~/Library/Logs/Receipts/de_holtwick_mac_homebrew_Receipts2.log

In the Mac App Store edition, the bundle identifier in the filename differs slightly.

AppleScript

The app can be automated entirely from AppleScript. Its terminology dictionary can be inspected in macOS Script Editor (File > Open Dictionary), and example scripts are available at GitHub holtwick/receipts-api.

Recognizing a library

A document’s name is only its display name: it depends on the location on disk and changes as soon as the library is moved or renamed, which makes it unsuitable as an identifier. Use workspace id instead - the identifier stored inside the library itself, which survives renaming and moving.

tell application "Receipts Space"
  set theLibrary to document 1
  set theId to workspace id of theLibrary
  set theLocation to file of theLibrary
end tell

Two things to keep in mind:

  • A copy of a library carries the same workspace id. Scripts that have to tell copies apart use workspace id together with file.
  • While a library is still opening, the property returns missing value.

Exporting receipts

export returns the receipts of the open library as JSON - in the same schema as the JSON import, so with id (the uid), amounts, category, contact, tags, and references to the file (asset with path and url, assetOriginal for the originally imported file).

tell application "Receipts Space"
  set theJSON to export category "Business Meals" as json
end tell

All filters are optional and can be combined - a receipt then has to match all of them:

ParameterMeaning
from / toDate range (AppleScript date).
whereWhich date the range refers to: date sent (receipt date, default), date paid, or date created.
with paid / with mark / with confirmedOnly paid, marked, or confirmed receipts.
category / contact / tagOnly receipts with the category, contact, or tag of this name. The name has to match exactly, ignoring case. An unknown name returns an empty list. Since 4.0.
inAlso write the result to this file.

Leave out `items`

Receipts are the default. Please do not write the word items: AppleScript reads it as its own keyword (“every item”), and the command fails. For the other kinds of data it works: export categories as json, export contacts as json, export tags as json.

Importing files and data

import creates a new receipt. type states what is passed:

tell application "Receipts Space"
  import "/Users/me/Downloads/invoice.pdf" type path
  import "https://example.com/invoice.pdf" type url
  import "{\"title\": \"Parking ticket\", \"date\": \"2026-09-01\"}" type json
end tell
  • type url (default) - a local or remote URL.
  • type path - an absolute file path.
  • type json - a receipt in the JSON import format. If the JSON contains the id of an existing receipt, only the given fields change - this is how scripts set title, category, contact, tags, and so on (see Updating Existing Documents). Before version 4.0, type json failed with an error when called from AppleScript.
  • type mail - the source of an email.

The import runs in the background; the command returns right away.

Replacing the file of a receipt

replace file swaps the file of an existing receipt - for example for an edited or extended version. Since 4.0.

tell application "Receipts Space"
  replace file "/Users/me/Desktop/business-meal.pdf" for uid "..."
end tell
  • The receipt keeps all its data: date, amounts, contact, category, tags, and notes. No values are taken from the new file; only its text is recognized again (for search).
  • No second entry is created, and no duplicate question comes up.
  • A file works as well as a path: replace file (POSIX file "/...") for uid "...".
  • The command waits until the file is processed and returns the uid. Errors - unknown uid, missing file, unsupported file type - arrive as script errors and can be caught with try.
  • Receipts protected by the revision safety mode cannot be changed.

The uid of a receipt is the id field in the result of export.

URL Scheme receipts-space://import

Imports a single PDF file whose binary data is passed directly inside the URL. Can be invoked from a browser, mail client, Shortcuts, or any other app.

ParameterTypeMeaning
contentString (Base64-URL)Binary data of the PDF, encoded as RFC 4648 §5 Base64-URL (-/_ instead of +//, padding = optional).

Example:

receipts-space://import?content=JVBERi0xLjQKJeLjz9MKCg...

Notes:

  • The payload is always treated as PDF (UTI com.adobe.pdf). Other file types are not supported by this endpoint - for images, e-mails, or structured data, use the JSON import or a file-based hand-off instead.
  • If content is missing or cannot be decoded, the call is silently dropped.
  • URL length is limited by the host system (typically a few MB via open, considerably less in browsers). For larger files, a file-based import is more robust.
  • The import runs as a manual session through the regular import pipeline; duplicate detection, folder watching, etc. behave exactly as with a file import.
  • Additional metadata (title, category, amounts, id, …) cannot be supplied through this scheme. For metadata, use the JSON import (e.g. via a .receipts-import file or a .receipts-package).

File Extensions

Receipts Space registers itself with macOS for a number of file extensions. When such a file is opened (double click, drag & drop onto the app icon, open in Terminal), the app responds according to the extension. The most relevant ones:

ExtensionRoleMeaning
.receipts-importImporter (JSON)Explicit extension for a JSON file in the format described above. Recognized unambiguously as an import - no risk of confusion with other .json files.
.receipts-packageImporter (package)Import package as a bundle: combines a JSON description with one or more asset files in a single container that can be handled via drag & drop.
.jsonImporter (JSON)A regular JSON file. Inspected and, if it matches the document schema, imported.
.pdfImporterAdded as a new document with the PDF as its asset; text is extracted.
.png, .jpg, .jpeg, .tif, .tiff, .gifImporterThe image is imported as an asset; if OCR is enabled, the text is recognized as well.
.eml, .emlxImporterE-mail file; attachments are imported as receipts, the message body is retained as context.
.xmlViewerStructured XML data (e.g. ZUGFeRD / X-Rechnung attachments) is parsed where supported.

Structure of a .receipts-package

A .receipts-package is a folder bundle with the following layout:

MyPackage.receipts-package/
├── Info.json
└── Files/
    ├── invoice-1.pdf
    └── scan-2.jpg

Info.json contains the manifest:

{
  "note": "Optional note, attached to every document as its notes field.",
  "files": [
    {
      "filename": "invoice-1.pdf",
      "title": "Hotel invoice Berlin",
      "url": "https://example.com/invoice/42"
    },
    {
      "filename": "scan-2.jpg"
    }
  ]
}
  • filename refers to a file inside Files/.
  • title (optional) is used as the suggested title, e.g. a page title from Safari.
  • url (optional) is stored as the source URL on the document; may also be used on its own (without filename), in which case the URL is imported directly.

Auto-cleanup: ReceiptsMove- prefix

File names starting with ReceiptsMove- are moved to the trash automatically after a successful import. The mechanism was originally intended for Mail.app temporary files but works generically:

ReceiptsMove-invoice.pdf

If moving into the system trash fails (e.g. on network volumes), a fallback folder named .ReceiptsTrash is created next to the file and used instead.

JSON Import in Receipts Space

Receipts Space can import documents from JSON files. This section describes the supported format and all recognized fields.

Invocation

A JSON import is triggered as soon as a file with UTI public.json is passed to the app (drag & drop, folder watching, File > Open, AppleScript, URL scheme, etc.). The file is recognized in import-from-url.swift and forwarded to the JSON importer in import-from-json.swift.

The actual mapping of JSON fields onto a DocumentSchema happens in document-json-read.swift. All valid keys are defined as an enum in document-json-keys.swift.

Structure of the JSON File

The JSON file contains either a single document object or an array of document objects. Arrays are flattened automatically; each element is imported as its own document.

[
  { "id": "…", "title": "…", … },
  { "id": "…", "title": "…", … }
]

Fields

Identification

KeyTypeMeaning
idStringUnique UID of the document. If it already exists in the library, the document is treated as a duplicate (see below). If missing, a new UID is generated.
titleStringTitle of the document.
viaStringSource / import channel (e.g. "mail", "scan"). Otherwise taken from the import session or defaults to "json".
referenceStringReference number (invoice / receipt number), mapped to the name field.
notesStringFree-form notes.
textStringExtracted text content of the document (e.g. OCR result). Also extracted automatically from the asset if the asset is a PDF and text was not supplied.
doctypeStringDocument type (internal; overridden when isCredit = true).

Flags

KeyTypeMeaning
isConfirmedBool“Confirmed” flag.
isMarkedBoolStar / flag.
isCreditBoolCredit entry. Also resets doctype.

Dates

Date fields accept a native date object, an ISO 8601 timestamp as the export writes it (2026-09-01T12:00:00.000Z), or, since 4.0, a plain day (2026-09-01).

KeyTypeMeaning
dateISO dateDocument date.
datePaymentISO datePayment date.
dateAddedISO dateCreation date. Ignored for duplicates.

Amounts

Amounts are stored in two blocks: amountsOriginal (the original currency of the receipt) and amounts (converted into the library’s default currency).

"amountsOriginal": {
  "currency": "USD",
  "gross": 123.45,
  "tax": 19.70,
  "taxDetails": [
    { "percent": 19, "value": 19.70 }
  ]
},
"amounts": {
  "gross": 115.12,
  "exchangeRate": 0.9325
}
Key (inside amountsOriginal)TypeMeaning
currencyString (ISO 4217)Original currency of the receipt.
grossNumber/StringGross amount. Rounded to two decimal places.
taxNumber/StringTotal tax (optional).
taxDetailsArrayIndividual tax rates. Either as objects { "percent": …, "value": … } or as tuple arrays [percent, value].
Key (inside amounts)TypeMeaning
grossNumber/StringGross amount in the default currency. Only used when the original currency differs from the default.
exchangeRateNumber/StringExplicit exchange rate. If missing, a rate is fetched asynchronously after import.

Relations

KeyTypeMeaning
categoryString or objectCategory. Either as a string (= title) or as an object { "id": "…", "title": "…" }. With id, an existing category is matched by UID; otherwise by title (a new one is created if necessary).
contactString or objectContact / merchant. Same format as category.
providerString or objectAlias for contact, recognized only on import.
tagsArrayTags. Either ["Travel", "Vacation"] or [{ "title": "Travel" }, …].

IBAN

KeyTypeMeaning
ibanStringIBAN. E-mail-like values (containing @) are ignored (workaround for PayPal data).

Assets (File Attachment)

The actual receipt document is attached as asset, optionally with the untouched original under assetOriginal (e.g. the unmodified scan PDF before OCR).

"asset": {
  "name": "invoice.pdf",
  "uti": "com.adobe.pdf",
  "mime": "application/pdf",
  "data": "<base64>"
}
KeyTypeMeaning
nameStringFile name (default: "unnamed").
utiStringUniform Type Identifier. Used to derive the MIME type if that is missing.
mimeStringMIME type; overrides the value derived from uti.
dataData or Base64 stringDirect binary data (preferred).
fileurlString (URL)Fallback 1: URL the data is read from.
pathStringFallback 2: path to a local file. Interpreted as an absolute path first; when the JSON import comes from a file, the path is also resolved relative to that file’s directory.
urlString (URL)Fallback 3: additional URL source.

The sources are tried in the order shown; the first one that yields non-empty data is used. For PDF assets, the text content is also extracted unless text has already been set.

Export/Import Round-trip

The JSON export of a document (drag & drop, clipboard, AppleScript export command) uses the same schema as the import and can be fed straight back in. Exported and imported fields share the same key enum internally, so an export → import round-trip brings the data back without loss. Assets on export are referenced either as url (internal web server), fileurl, or path; on import the first available source is used.

Updating Existing Documents

A JSON import with an id that already exists in the library overwrites the given fields on the existing document - the endpoint doubles as an update interface. Fields not mentioned remain unchanged.

By default the document is marked as a duplicate (status “Duplicate” in the list). For a silent update without that marker:

{
  "id": "…existing-uid…",
  "title": "New title",
  "notes": "Amended note",
  "onDuplicateFlag": false
}

onDuplicateIncludeKeys limits the update to specific fields; onDuplicateExcludeKeys does the opposite. Details in the next section.

Duplicate Handling

If a document with the same id already exists in the library, the duplicate logic kicks in:

  • Document was permanently deleted: it is restored and treated as a new import.
  • Document was in the trash (archived): it is marked as a duplicate. Behavior is controlled by the keys below.
KeyTypeDefaultMeaning
onDuplicateSkipBoolfalseSkip the duplicate entirely; no update is performed.
onDuplicateUnarchiveBooltrueBring an archived duplicate back out of the trash.
onDuplicateFlagBooltrueMark the duplicate as such (status code STATUS_CODE_DUPLICATE).
onDuplicateIncludeKeysArray<String>-If set: only these keys from the JSON are applied; all others are ignored.
onDuplicateExcludeKeysArray<String>-These keys are ignored for duplicates.

The dateAdded field is never applied to duplicates, to preserve the original creation date.

Example

{
  "title": "Office supplies",
  "via": "scan",
  "reference": "RE-2025-00123",
  "date": "2025-11-14",
  "datePayment": "2025-11-18",
  "isConfirmed": true,
  "amountsOriginal": {
    "currency": "EUR",
    "gross": 42.80,
    "taxDetails": [
      { "percent": 19, "value": 6.83 }
    ]
  },
  "category": "Office",
  "contact": { "title": "Müller GmbH" },
  "tags": ["Q4", "Consumables"],
  "iban": "DE89370400440532013000",
  "asset": {
    "path": "re-2025-00123.pdf",    
  }
}