# Introduction (/docs)
---
title: Introduction
description: Automate invoice collection from web portals and emails with Invoice Radar.
---
## What is Invoice Radar?
Invoice Radar automates invoice collection from web portals and emails, saving you hours of manual work. Whether you're managing personal finances or business accounting, Invoice Radar streamlines the entire process of gathering and organizing your financial documents.
[Request Access to Invoice Radar](https://invoiceradar.com/) Β· [Browse Community Plugins](https://github.com/invoiceradar/plugins/tree/main/plugins)
## Key Features
- **π€ Automated Collection** - Downloads invoices from web portals and scans email inboxes with intelligent document recognition.
- **π Privacy & Security First** - Data collection happens locally on your computer. Your credentials never leave your device.
- **β¨ AI Document Processing** - Extract key information from your invoices using AI
- **π€ Automatic Exports** - Forward invoices to accounting software like sevdesk, Lexware, and BuchhaltungsButler.
## Documentation
### Core Features
- **[Invoices from Web Portals](/docs/invoices-from-web-portals)** - Automated invoice collection from web portals
- **[Invoices from Emails](/docs/invoices-from-emails)** - Collect invoices from your email accounts
- **[Automatic Export](/docs/export)** - Automatically forward invoices to accounting software
- **[Security & Privacy](/docs/security-privacy)** - How Invoice Radar protects your data
### Plugin Development
- **[Creating Your First Plugin](/docs/plugins)** - Get started with plugin development
- **[Plugin Reference](/docs/plugin-reference)** - Complete plugin structure and configuration
- **[Steps Reference](/docs/steps-reference)** - All available plugin actions
- **[Useful Patterns](/docs/patterns)** - Common solutions and best practices
## Contributing
Invoice Radar thrives on community contributions. Create plugins for new services, report issues, or suggest improvements on [GitHub](https://github.com/invoiceradar/plugins).
---
Ready to get started? Check out the documentation sections above to find what you're looking for.
# Invoices from Emails (/docs/invoices-from-emails)
---
title: Invoices from Emails
description: Automatically collect invoices from your email accounts with secure SMTP and Google integration
icon: emails
---
## How it works
Invoice Radar finds invoices across all your connected email accounts and displays them in a **unified inbox**. When you review an invoice, you have two options:
- **Accept a single invoice** - Approve this invoice without any future automation
- **Trust the seller** - Automatically accept all future invoices from this sender
This gives you complete control over which companies you trust for automatic collection.
## Connecting your email accounts
To set up your unified inbox, you need to connect your email accounts to Invoice Radar. You can connect your email accounts using SMTP or OAuth.
#### SMTP connection
SMTP connection works with any email provider (Gmail, Outlook, Yahoo, or your business email). It connects directly to your email server just like any email app on your phone or computer. Your emails stay private and are processed entirely on your device.
#### Login with Google (Gmail)
You can sign in with Google to connect to your email. You'll authenticate directly with Google, and you can revoke access anytime from your Google account settings. The authentication happens directly between the app and Google with no server in between, ensuring there's no way for us to read your email.
#### Login with Microsoft (Outlook)
You can connect your Microsoft account with secure OAuth authentication. The connection is made directly between the app and Microsoft, ensuring your email data never passes through our servers.
## Your email stays private
All email scanning happens locally on your computer. Invoice Radar only saves the invoice attachments you approve - your actual emails are never stored or transmitted anywhere. Connections to your email provider use TLS/SSL encryption, just like any secure email client.
When using OAuth for Google or Microsoft, the authentication happens directly between the app and the email provider, meaning no data passes through our servers.
For more details about how Invoice Radar protects your data, see our [Security & Privacy](/docs/security-privacy) guide.
## Automatic downloads from payment providers
Some companies like **Paddle** and **SumUp** send you email notifications with download links instead of attaching the actual invoice PDF. Invoice Radar handles this automatically.
When you receive a Paddle invoice notification, Invoice Radar will detect it, visit the Paddle portal using the link in your email, and download the actual PDF for you. SumUp receipts work the same way.
More payment providers will be supported in the future.
## Configuration Options
You can customize how Invoice Radar scans your email:
- **Folder Selection** - Scan specific folders like "Inbox" or "Receipts" instead of all folders
- **Email Address Filtering** - Filter by recipient address when multiple emails forward to the same inbox (e.g., billing@company.com)
## FAQ
### Microsoft: "Needs admin approval" error
If you're using Microsoft 365 or Outlook with a business or organization account, you might see a "Needs admin approval" error when trying to connect your email. This happens because your organization's IT administrator needs to approve Invoice Radar before employees can use it.
**Solution for IT administrators:**
To approve Invoice Radar for your organization, an administrator needs to grant consent by visiting this link:
```
https://login.microsoftonline.com/common/adminconsent?client_id=7e59d229-c9e5-44da-9199-ef6c2f94b171&redirect_uri=https://invoiceradar.com/microsoft/admin-consent/success&scope=https://graph.microsoft.com/.default
```
After the administrator completes the approval process, all users in the organization will be able to connect their Microsoft email accounts to Invoice Radar.
**What permissions are requested:**
- `offline_access` - Allows the app to maintain access without requiring frequent re-authentication
- `email` - Read your email address
- `openid` - Sign you in and read your basic profile
- `IMAP.AccessAsUser.All` - Read-only access to your mailbox via IMAP to scan for invoices
# Invoices from Web Portals (/docs/invoices-from-web-portals)
---
title: Invoices from Web Portals
description: Automatically collect invoices from web portals using Invoice Radar
icon: web-portals
---
## Overview
Invoice Radar helps you collect invoices from web portals in bulk. The only thing you need to do is to select the service, log in and you're done.
## How It Works
Getting started with Invoice Radar is simple:
1. **Choose a Service** - Select from over 100 pre-built plugins for popular services like Amazon, ChatGPT, AWS, and more
2. **Press Start** - Invoice Radar will navigate to the service, log in, and collect your invoices automatically
3. **Optional: Add Credentials** - For convenience, you can securely store your login details to enable automatic authentication
That's it! Invoice Radar handles all the complex web automation behind the scenes. You can also create [your own plugins](/docs/plugins) to support any service you use.
## Secure by default
You have full control over how Invoice Radar handles your credentials. In any case, no credentials are shared with us, making it as secure as using your regular browser.
### A. Automatic Login
Store your credentials and your two-factor authentication (OTP) secrets for completely hands-free operation.
### B. Enter OTPs Manually
Store your credentials, but enter your two-factor authentication (OTP) manually each time you collect invoices.
### C. Manual Login
Log in manually each time you collect invoices. Invoice Radar never touches your credentials - it's exactly like using a regular browser.
## Supported Services
Invoice Radar supports a wide range of services including:
- Cloud providers (AWS, Google Cloud, Azure)
- Advertising platforms (Google Ads, Meta Ads, etc.)
- AI platforms (ChatGPT, Claude, etc.)
- And many more...
You can find a full list of supported services in [website](/). You can also browse our [community plugins](https://github.com/invoiceradar/plugins/tree/main/plugins) or learn how to [create your own](/docs/plugins).
## Security & Privacy
All automation happens locally on your computer. Invoice Radar never sends your credentials or financial data to external servers - it's as secure as using your regular browser.
For detailed security information, see our [Security & Privacy](/docs/security-privacy) guide.
## Getting Started
[Request access to Invoice Radar](https://invoiceradar.com/) to start automating your invoice collection from web portals.
Check out our [Plugin Development](/docs/plugins) guide to extend Invoice Radar with custom integrations.
# Security & Privacy (/docs/security-privacy)
---
title: Security & Privacy
description: How Invoice Radar protects your data through local processing and end-to-end encryption
icon: lock
---
## Core Security Principles
Invoice Radar is built with a privacy-first architecture. Unlike cloud solutions, all collection happens locally on your computer - similar to how your web browser works. Your sensitive data stays fully private with our end-to-end encrypted synchronization.
## Runs locally on your computer
When you collect invoices with Invoice Radar, it does not depend on any external services. It runs locally on your computer, ensuring your data stays private.
**Key security benefits:**
- Your login credentials never leave your computer
- No need to share passwords or emails with external services
- Full control over your sensitive financial data
## Secure Syncing
To allow you to access your data from multiple devices and collaborate with team members, Invoice Radar synchronizes your data using end-to-end encryption. All your data is encrypted on your device using a memorable passphrase that serves as your account login, ensuring that your data remains unreadable to anyone without your passphrase.
Passwords and login sessions stay on each device only. Even if our servers get hacked, your data stays safe without your passphrase.
## Automatic Export
When you use [automatic export](/docs/export) to forward invoices to accounting software, your API credentials are encrypted and synced across your organization's devices using the same end-to-end encryption as your other data.
**Security measures:**
- **Direct connections** - All destinationsΒΉ connect directly from your device to the service provider's API with no intermediary
- **Encrypted sync** - API keys are encrypted and synced across your organization using end-to-end encryption
- **HTTPS/TLS** - All API calls use secure encryption
ΒΉ Except for email forwarding, which is uses Invoice Radar's email server for sending the invoices. No data is stored on our servers.
## AI Data Extraction
Invoice Radar offers optional AI-powered data extraction to automatically identify key information from your invoices, such as vendor details, amounts, and dates.
This feature is completely opt-in and designed with privacy in mind. When used, your invoice documents are sent to our AI service for processing, then immediately returned to your device. No data is stored on our servers.
# Useful Patterns (/docs/patterns)
---
title: Useful Patterns
description: Common patterns and best practices for Invoice Radar plugin development
---
## Authentication Patterns
### Common patterns for authentication checks (`checkAuth`)
#### Pattern 1: Go to Dashboard and Check URL
Many services automatically redirect to the login page if the user is not authenticated. We can use this behavior to check if the user is authenticated.
```json
{
"action": "navigate",
"url": "https://example.com/login"
},
{
"action": "checkURL",
"url": "https://example.com/account"
}
```
Depending on the service, they may redirect you from the dashboard to the login page if you are not authenticated. In this case, you can use the `checkURL` step to check if the URL still matches after visiting the dashboard.
```json
{
"action": "navigate",
"url": "https://example.com/dashboard"
},
{
"action": "checkURL",
"url": "https://example.com/dashboard"
}
```
Note that you can use glob patterns to match dynamic URLs: `https://example.com/dashboard/**`.
#### Pattern 2: Check for Logout Button
You can use a selector that is unique to the authenticated state to check if the user is authenticated, e.g. a logout button or profile link.
```json
{
"action": "navigate",
"url": "https://example.com/home"
},
{
"action": "waitForElement",
"selector": "#logout-button"
}
```
#### Pattern 3: Wait for Condition
You can use the `waitForCondition` step to wait for a custom JavaScript condition to become true.
```json
{
"action": "waitForCondition",
"script": "document.cookie.includes('auth_token=')"
}
```
#### Tip: Make sure the website is fully loaded
In some cases, the website has not fully loaded when the `checkElementExists` step is executed. To avoid this, you can use the `waitForNetworkIdle` attribute to wait for the page to be fully loaded.
```json
{
"action": "navigate",
"url": "https://example.com/home",
"waitForNetworkIdle": true
},
{
"action": "checkElementExists",
"selector": "#logout-button"
}
```
### Common patterns for start authentication (`startAuth`)
#### Pattern 1: Go to Login Page and wait for logged in state
Most authentication processes start by navigating to the login page and waiting for a specific element to appear after a successful login.
Remember that the browser will be visible during the authentication process, allowing the user to interact with the login form. The authentication flow itself can be automated, but isn't required.
```json
{
"action": "navigate",
"url": "https://example.com/login"
},
{
"action": "waitForElement",
"selector": "#logout-button"
}
```
To give the user enough time to log in, it's recommended to provide a long timeout to the wait step, with a default of 120 seconds.
## Advanced Patterns
### Running a fetch request
Sometimes, you might need to run a fetch request inside a step to fetch data from an API. To do this, you can use the `extractAll` action.
```json
{
"action": "extractAll",
"variable": "invoice",
"script": "fetch('https://example.com/api/invoices').then(res => res.json())",
"forEach": [
{
"action": "downloadPdf",
"url": "{{invoice.url}}",
"document": {
"id": "{{invoice.id}}",
"date": "{{invoice.date}}",
"total": "{{invoice.total}}"
}
}
]
}
```
This will run the fetch request and return the result as a JavaScript object.
### Paginating with a "Next" button
Use selector pagination when the UI has a next button or link.
```json
{
"action": "extractAll",
"selector": ".invoice-row",
"fields": {
"id": ".invoice-id",
"date": ".invoice-date",
"total": ".invoice-total",
"url": ".invoice-link[href]"
},
"pagination": {
"selector": "button.next-page",
"waitForSelector": ".invoice-row"
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{item.url}}",
"document": {
"id": "{{item.id}}",
"date": "{{item.date}}",
"total": "{{item.total}}"
}
}
]
}
```
If `waitForSelector` is omitted, Invoice Radar waits for network idle after clicking the next selector.
### Cursor-based pagination (script)
Use script pagination for APIs that return a cursor or `nextPage` token. It also works for selector-based extraction when you want custom logic to decide whether to paginate (and can optionally click "next" inside the script).
```json
{
"action": "extractAll",
"script": "async (prev) => { const cursor = prev?.nextCursor; return fetch(`https://example.com/api/invoices?cursor=${cursor ?? ''}`).then(res => res.json()) }",
"transform": "(result) => result.items",
"pagination": {
"script": "(result) => Boolean(result.nextCursor)",
"behavior": "paginateThenProcess"
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{item.pdfUrl}}",
"document": "{{item}}"
}
]
}
```
In this pattern:
- The `extractAll` script receives the previous raw result as `prev`.
- The `pagination.script` receives the current raw result and returns `true` to continue.
- `transform` maps the raw response to an array of items.
For selector-based `extractAll`, `pagination.script` receives the extracted items array (after `transform`) and can also perform pagination actions like clicking a "next" button before returning `true`.
### Run steps inside an ``
In some scenarios, you might need to run a step inside an `` element. To do this, you can use the `iframe` attribute on the step.
```json
{
"action": "click",
"selector": "#button-inside-iframe",
"iframe": true
}
```
By setting `iframe` to `true`, Invoice Radar will find the first `` element on the page and run the step inside it.
You can also use a string that is contained inside the iframe's `src` attribute to target a specific iframe.
```json
{
"action": "click",
"selector": "#button-inside-iframe",
"iframe": "iframe.example.com"
}
```
### Use Network Idle Wisely
Only use `waitForNetworkIdle` when necessary, as it can slow down execution:
```json
{
"action": "navigate",
"url": "https://example.com/dashboard",
"waitForNetworkIdle": true
}
```
Use it for:
- Pages with heavy AJAX loading
- Single-page applications
- When authentication state depends on network requests
# Plugin Reference (/docs/plugin-reference)
---
title: Plugin Reference
description: Complete reference for creating JSON-based plugins in Invoice Radar
---
## Overview
Invoice Radar plugins are defined using JSON configuration files that specify how to authenticate with a service and extract documents. This reference covers the complete plugin structure and configuration options.
## Plugin Structure
A plugin consists of several main sections:
```json
{
"$schema": "https://raw.githubusercontent.com/invoiceradar/plugins/main/schema.json",
"id": "example-service",
"name": "Example Service",
"description": "Short description of the Example Service",
"homepage": "https://example.com",
"autofill": true,
"configSchema": {
// Configuration options
},
"startAuth": [
// Authentication steps
],
"checkAuth": [
// Authentication verification steps
],
"getConfigOptions": [
// Optional: Extract configuration options
],
"getDocuments": [
// Document extraction steps
]
}
```
## Core Properties
### Basic Metadata
| Property | Type | Required | Description |
| ------------- | -------- | -------- | ---------------------------------------------- |
| `$schema` | `string` | No | URL to the JSON schema for validation |
| `id` | `string` | **Yes** | Unique identifier for the plugin |
| `name` | `string` | **Yes** | Display name of the plugin |
| `description` | `string` | No | Brief description of what the plugin does |
| `homepage` | `string` | No | URL of the service (used for logo and linking) |
## Configuration Schema
Define user-configurable options using the `configSchema` property:
```json
{
"configSchema": {
"teamId": {
"type": "string",
"title": "Team ID",
"description": "The ID of your team",
"required": true,
"example": "team_abc123"
},
"apiKey": {
"type": "password",
"title": "API Key",
"description": "Your API key for authentication",
"required": true
},
"environment": {
"type": "string",
"title": "Environment",
"description": "Select the environment",
"options": [
{ "value": "production", "label": "Production" },
{ "value": "staging", "label": "Staging" }
],
"default": "production"
},
"downloadReceipts": {
"type": "boolean",
"title": "Download Receipts",
"description": "Download receipts for each invoice",
"default": false
}
}
}
```
### Configuration Property Types
| Type | TypeScript Type | Description |
| ---------- | --------------- | ----------------------------- |
| `string` | `string` | Text input field |
| `password` | `string` | Password input field (masked) |
| `number` | `number` | Numeric input field |
| `boolean` | `boolean` | Checkbox input |
### Configuration Property Options
| Property | Type | Description |
| ------------- | --------------------------------------- | ------------------------------ |
| `title` | `string` | Display label for the field |
| `description` | `string` | Help text explaining the field |
| `required` | `boolean` | Whether the field is required |
| `default` | `string \| number \| boolean` | Default value |
| `example` | `string` | Example value to show users |
| `hidden` | `boolean` | Hide the field from users |
| `options` | `Array<{value: string, label: string}>` | Dropdown options |
## Plugin Flows
### Authentication Flow (`startAuth`)
Steps to authenticate a user who isn't logged in:
```json
{
"startAuth": [
{
"action": "navigate",
"url": "https://example.com/login"
},
{
"action": "waitForElement",
"selector": "#login-form"
}
]
}
```
### Authentication Check (`checkAuth`)
Steps to verify if a user is already authenticated:
```json
{
"checkAuth": [
{
"action": "navigate",
"url": "https://example.com/dashboard"
},
{
"action": "checkElementExists",
"selector": "#user-menu"
}
]
}
```
### Configuration Options (`getConfigOptions`)
Optional steps to extract configuration options like team IDs or project lists. These will be exposed to the user as a dropdown in the configuration modal.
```json
{
"configSchema": {
"teamId": {
"type": "string",
"title": "Team ID",
"description": "The ID of your team",
"required": true,
"example": "team_abc123"
}
},
"getConfigOptions": [
{
"action": "navigate",
"url": "https://example.com/settings"
},
{
"action": "extractAll",
"selector": ".team-item",
"variable": "team",
"fields": {
"id": ".team-id",
"name": ".team-name"
},
"forEach": [
{
"action": "exposeOption",
"config": "teamId",
"option": "{{team}}"
}
]
}
]
}
```
### Document Extraction (`getDocuments`)
Steps to find and download documents:
```json
{
"getDocuments": [
{
"action": "navigate",
"url": "https://example.com/invoices"
},
{
"action": "extractAll",
"selector": ".invoice-row",
"variable": "invoice",
"fields": {
"id": ".invoice-id",
"date": ".invoice-date",
"total": ".invoice-total",
"url": ".download-link[href]"
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{invoice.url}}",
"document": "{{invoice}}"
}
]
}
]
}
```
## Using Configuration Variables
Access user configuration within steps using template syntax. Config values are accessed via the `config` object:
```json
{
"action": "navigate",
"url": "https://example.com/team/{{config.teamId}}/invoices"
}
```
## Step Reference
For detailed information about available steps, see the [Steps Reference](/docs/steps-reference) documentation.
## Autofill Configuration
Invoice Radar automatically detects and fills provided login credentials (username, password, OTP) and submits the authentication form to streamline the login process.
### Default Behavior
By default, `autofill` is set to `true`, which enables automatic input detection for:
- **Username** (also works for email fields)
- **Password**
- **OTP** (One-Time Password/2FA codes)
- **Submit** (automatically submits the form after filling credentials)
When enabled, Invoice Radar will automatically detect and fill these fields during the authentication process, then submit the form to complete the login.
```json
{
"autofill": true
}
```
### Customization Options
You can customize or disable autofill for individual fields by setting each property (`username`, `password`, `otp`, `submit`) to either:
- **`false`** - Disables autofill for that specific field
- **Configuration object** - Customizes the behavior with these options:
- `selector`: Custom selector (CSS or XPath) if the default detection doesn't work. XPath selectors must start with `/`, `./`, or `.[`
- `title`: Custom label for the field in the UI (e.g., "Email" instead of "Username")
### Autofill Field Options
| Field | Type | Description |
| ---------- | ------------------- | ------------------------------------- |
| `username` | `boolean \| object` | Username/email field configuration |
| `password` | `boolean \| object` | Password field configuration |
| `otp` | `boolean \| object` | One-time password field configuration |
| `submit` | `boolean \| object` | Submit button configuration |
Each field can be:
- `true`: Enable with automatic detection
- `false`: Disable the field
- `object`: Custom configuration with `selector` and optional `title`
### Configuration Examples
#### Disable Automatic Submission
```json
{
"autofill": {
"submit": false
}
}
```
#### Custom Selectors
In case the default autofill doesn't work, you can provide custom selectors for each field.
```json
{
"autofill": {
"username": {
"selector": "#email-input",
"title": "Email Address"
},
"password": {
"selector": "#password-input"
},
"otp": false,
"submit": {
"selector": "#login-button"
}
}
}
```
### User Agent Override
```json
{
"userAgentOverride": "Mozilla/5.0 (compatible; CustomBot/1.0)"
}
```
You can override the browser's user agent string. Set to `true` to use a default override, or provide a custom string.
## Examples
### Basic Plugin
```json
{
"id": "simple-service",
"name": "Simple Service",
"description": "Short description of the Simple Service",
"homepage": "https://simple.com",
"startAuth": [
{
"action": "navigate",
"url": "https://simple.com/login"
}
],
"checkAuth": [
{
"action": "checkURL",
"url": "https://simple.com/dashboard"
}
],
"getDocuments": [
{
"action": "navigate",
"url": "https://simple.com/invoices"
},
{
"action": "extractAll",
"selector": ".invoice",
"variable": "invoice",
"fields": {
"id": ".id",
"date": ".date",
"total": ".total"
},
"forEach": [
{
"action": "printPdf",
"document": "{{invoice}}"
}
]
}
]
}
```
### Advanced Plugin with Configuration
```json
{
"id": "advanced-service",
"name": "Advanced Service",
"description": "Extract invoices with team support",
"homepage": "https://advanced.com",
"autofill": {
"username": {
"title": "Email"
},
"submit": {
"selector": "#sbmt-btn"
}
},
"configSchema": {
"teamId": {
"type": "string",
"title": "Team ID",
"required": true
},
"includeArchived": {
"type": "boolean",
"title": "Include Archived Invoices",
"default": false
}
},
"autofill": {
"username": {
"selector": "#email",
"title": "Email"
},
"password": true,
"otp": false
},
"startAuth": [
{
"action": "navigate",
"url": "https://advanced.com/login"
}
],
"checkAuth": [
{
"action": "checkElementExists",
"selector": "#user-avatar"
}
],
"getConfigOptions": [
{
"action": "extractAll",
"selector": ".team-option",
"variable": "team",
"fields": {
"value": "[data-team-id]",
"label": ".team-name"
},
"forEach": [
{
"action": "exposeOption",
"config": "teamId",
"option": "{{team}}"
}
]
}
],
"getDocuments": [
{
"action": "navigate",
"url": "https://advanced.com/{{teamId}}/invoices"
},
{
"action": "if",
"script": "'{{includeArchived}}' === 'true'",
"then": [
{
"action": "click",
"selector": "#include-archived"
}
]
},
{
"action": "extractAll",
"selector": ".invoice-item",
"variable": "invoice",
"fields": {
"id": ".invoice-number",
"date": ".invoice-date",
"total": ".invoice-amount",
"downloadUrl": ".download-btn[href]"
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{invoice.downloadUrl}}",
"document": {
"id": "{{invoice.id}}",
"date": "{{invoice.date}}",
"total": "{{invoice.total}}"
}
}
]
}
]
}
```
## Validation
Plugins are validated against the JSON schema. Common validation errors include:
- Missing required properties (`id`, `name`, `startAuth`, `checkAuth`, `getDocuments`)
- Invalid step configurations
- Incorrect configuration schema types
- Missing `document` properties in PDF download steps
## Selector Syntax
Invoice Radar supports both **CSS selectors** and **XPath selectors** in the `selector` field:
- **CSS selectors** (default): Standard CSS syntax like `#id`, `.class`, `div > span`
- **XPath selectors**: Must start with `/`, `./`, or `.[` (e.g., `//button[text()='Submit']`, `.//span[contains(text(), 'Invoice')]`, `.[contains(@class, 'active')]`)
XPath is particularly useful for selecting elements by their text content, which is not possible with CSS selectors.
## Best Practices
1. **Use descriptive IDs**: Choose unique, descriptive plugin IDs
2. **Provide clear descriptions**: Help users understand what the service does
3. **Handle errors gracefully**: Use `optional: true` for non-critical steps
4. **Use proper selectors**: Prefer stable selectors over brittle ones. Both CSS and XPath are supported
5. **Test thoroughly**: Verify the plugin works across different scenarios
6. **Document configuration**: Provide clear descriptions and examples for config options
# Creating a Plugin (/docs/plugins)
---
title: Creating a Plugin
description: Learn how to create plugins for document collection in Invoice Radar
---
## Getting Started
For AI-assisted plugin development, see our LLM-optimized documentation: [/llms-full.txt](/llms-full.txt)
### Installation
1. **Download and Install Invoice Radar**:
- [Request Access to Invoice Radar](https://invoiceradar.com/)
2. **Download the Blank Plugin**:
- [Download the Blank Plugin](https://github.com/invoiceradar/plugins/blob/main/blank-plugin.json) to your local machine
- Rename the file to `your-plugin-name.json`
- Put it into a folder of your choice
3. **Add the Plugin to Invoice Radar**:
- Open Invoice Radar
- Navigate to `Plugins` tab inside settings
- Choose `Choose Plugin Directory` and select the folder where you saved the plugin
- Your plugin should now appear in the list of available plugins
## Plugin Overview
Plugins for Invoice Radar are written in JSON and follow a specific structure with these main sections:
- **Metadata**: Basic information (name, description, homepage)
- **Configuration**: Optional user-configurable options
- **Authentication**: Steps to log in and verify login status
- **Document Extraction**: Steps to find and download documents
For complete details on plugin structure and all available options, see the [Plugin Reference](/docs/plugin-reference).
## Minimal Plugin Example
Here's a basic plugin structure to get you started:
```json
{
"$schema": "https://raw.githubusercontent.com/invoiceradar/plugins/main/schema.json",
"id": "example",
"name": "Example Platform",
"description": "Short description of the service.",
"homepage": "https://example.com",
"checkAuth": [
{
"action": "navigate",
"url": "https://example.com/dashboard"
},
{
"action": "checkElementExists",
"selector": "#logout-button"
}
],
"startAuth": [
{
"action": "navigate",
"url": "https://example.com/login"
},
{
"action": "waitForElement",
"selector": "#account-summary"
}
],
"getDocuments": [
{
"action": "navigate",
"url": "https://example.com/billing"
},
{
"action": "extractAll",
"selector": ".invoice-row",
"variable": "invoice",
"fields": {
"id": {
"selector": ".invoice-id"
},
"date": {
"selector": ".invoice-date"
},
"total": {
"selector": ".invoice-total"
},
"url": {
"selector": ".invoice-download",
"attribute": "href"
}
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{invoice.url}}",
"document": "{{invoice}}"
}
]
}
]
}
```
## Writing Your First Plugin
### Step 1: Define Basic Information
Start with the essential metadata for your plugin:
```json
{
"id": "example-service",
"name": "Example Service",
"description": "Short description of the Example Service",
"homepage": "https://example.com"
}
```
The `id` should be unique and lowercase. The `homepage` URL is used to get the service favicon.
### Step 2: Set Up Authentication
Define how to check if a user is logged in (`checkAuth`) and how to start the login process (`startAuth`):
```json
"checkAuth": [
{
"action": "navigate",
"url": "https://example.com/dashboard"
},
{
"action": "checkElementExists",
"selector": "#logout-button"
}
],
"startAuth": [
{
"action": "navigate",
"url": "https://example.com/login"
},
{
"action": "waitForElement",
"selector": "#account-summary"
}
]
```
The browser will be visible during authentication, allowing users to interact with the login form.
For more authentication patterns and best practices, see the [Useful Patterns](/docs/patterns) guide.
### Step 3: Extract Documents
Define how to find and download documents:
```json
"getDocuments": [
{
"action": "navigate",
"url": "https://example.com/billing"
},
{
"action": "extractAll",
"selector": ".invoice-row",
"variable": "invoice",
"fields": {
"id": ".invoice-id",
"date": ".invoice-date",
"total": ".invoice-total",
"url": ".invoice-download[href]"
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{invoice.url}}",
"document": "{{invoice}}"
}
]
}
]
```
### Step 4: Save and Test
Save your plugin file and add it to Invoice Radar. You can now test the plugin to fetch documents from your service.
## Next Steps
Now that you've created your first plugin, explore these resources for advanced features:
- **[Plugin Reference](/docs/plugin-reference)** - Complete plugin structure, configuration options, and examples
- **[Steps Reference](/docs/steps-reference)** - Detailed documentation for all available actions
- **[Useful Patterns](/docs/patterns)** - Common patterns for authentication and data extraction
# Steps Reference (/docs/steps-reference)
---
title: Steps Reference
description: Complete reference for all available plugin steps in Invoice Radar
---
## Overview
Steps are the building blocks of Invoice Radar plugins. They define the sequence of actions that the browser automation engine will perform to authenticate with a service, navigate through pages, and extract documents. Each step represents a single action like clicking a button, typing text, or downloading a PDF.
Steps are executed sequentially within each plugin flow (`startAuth`, `checkAuth`, `getConfigOptions`, `getDocuments`), and support advanced features like conditional logic, loops, and data extraction.
## Step Structure
All steps share a common structure with an `action` property that defines the type of operation. E.g. `click`, `type`, `navigate`, `extract`, etc.
```json
{
"action": "click",
"selector": "#submit-button"
}
```
### Common Properties
Many steps support these common properties:
| Property | Type | Description |
| ------------- | --------- | ----------------------------------------------- |
| `action` | `string` | **Required.** The type of step to execute |
| `description` | `string` | Optional human-readable description of the step |
| `timeout` | `number` | Timeout in milliseconds (defaults vary by step) |
| `optional` | `boolean` | Whether the flow should continue if step fails |
| `iframe` | `string` | Target a specific iframe (selector or `true`) |
## Selectors
Steps that interact with or query page elements use the `selector` property. Invoice Radar supports two types of selectors:
### CSS Selectors
The default selector format. Use standard CSS selector syntax:
```json
{
"action": "click",
"selector": "#submit-button"
}
```
```json
{
"action": "waitForElement",
"selector": ".invoice-item:first-child"
}
```
### XPath Selectors
XPath selectors are also supported. Invoice Radar automatically detects XPath selectors when they start with `/`, `./`, or `.[`.
**Absolute XPath (from document root):**
Use `//` to search from the document root:
```json
{
"action": "click",
"selector": "//button[text()='Submit']"
}
```
**Relative XPath (from current element):**
Use `.//` or `.[` in extraction fields to search relative to the current element:
```json
{
"action": "extractAll",
"selector": "//div[@class='invoice-row']",
"variable": "invoice",
"fields": {
"id": ".//span[@class='invoice-id']",
"total": ".//span[@class='total']"
}
}
```
**Select by exact text match:**
XPath can select elements by their exact text content, which is not possible with CSS:
```json
{
"action": "click",
"selector": "//button[text()='Download Invoice']"
}
```
**Select by partial text match:**
Use `contains()` to match elements containing specific text:
```json
{
"action": "click",
"selector": "//*[contains(text(), 'Manage billing')]"
}
```
**Select by attribute value:**
```json
{
"action": "click",
"selector": "//a[contains(@href, '/invoices')]"
}
```
**Select parent or sibling elements:**
XPath allows you to navigate up the DOM tree or to siblings:
```json
{
"action": "click",
"selector": "//span[text()='Total:']/following-sibling::span"
}
```
**Good to know:**
- Invoice Radar automatically detects XPath selectors when they start with `/`, `./`, or `.[`
- `//` searches from the document root (absolute)
- `.//` searches relative to the current element (used in `extractAll` fields)
- `.[` is another form of relative XPath (e.g., `.[contains(@class, 'active')]`)
- XPath is particularly useful for selecting by text content, which CSS selectors cannot do
- Both selector types can be used interchangeably throughout your plugin
## Variables and Templating
Steps support variable interpolation using double curly braces `{{variableName}}`. Variables can be:
- **Configuration values**: Access user settings with `{{config.configKey}}`
- **Extracted data**: Use data from `extract` or `extractAll` steps
- **Built-in variables**: Like `{{index}}` in loops
- **Date variables**: `{{startDate.iso}}`, `{{startDate.year}}`, `{{currentDate.iso}}`, etc.
### Defining Variables in Steps
The primary way to dynamically define variables is through the [`extract`](#extract-extract) and [`extractAll`](#extract-all-extractall) steps:
- [**`extract`**](#extract-extract): Creates a single variable from page data or JavaScript execution
- [**`extractAll`**](#extract-all-extractall): Creates an array of variables by iterating over page elements, with each item available in the `forEach` steps
Once created, these variables can be referenced in any subsequent step using the templating syntax.
```json
{
"action": "navigate",
"url": "https://example.com/team/{{config.teamId}}/invoices"
}
```
## π Navigation Steps
### Navigate (`navigate`)
Navigates to the given URL and waits for the page to load. By default, it only waits for the initial page load, not for any subsequent AJAX requests.
```json
{
"action": "navigate",
"url": "https://example.com"
}
```
You can set `waitForNetworkIdle` to `true` to ensure the page is fully loaded before continuing.
```json
{
"action": "navigate",
"url": "https://example.com/dashboard",
"waitForNetworkIdle": true
}
```
**Good to know**:
- Relative URLs are supported and will be resolved based on the current page
- The navigate action will only wait for the initial page load, not for any subsequent AJAX requests
### Wait for URL (`waitForURL`)
Waits for the current URL to match the given URL, optionally with a timeout. Supports wildcards.
```json
{
"action": "waitForURL",
"url": "https://example.com/profile/**",
"timeout": 3000
}
```
### Wait for Element (`waitForElement`)
Waits for the given selector to appear on the page, optionally with a timeout.
```json
{
"action": "waitForElement",
"selector": "#example",
"timeout": 3000
}
```
### Wait for Navigation (`waitForNavigation`)
Waits for the page navigation to happen. This step will not wait for the page to be fully loaded. Use the [waitForNetworkIdle](#wait-for-network-idle-waitfornetworkidle) step for that purpose. Timeout is optional and defaults to 10 seconds.
```json
{
"action": "waitForNavigation",
"timeout": 10000
}
```
### Wait for Network Idle (`waitForNetworkIdle`)
Waits for the network to be idle. This is useful if you want to ensure the page has finished loading all resources. The step completes when there are no more network requests for 500ms. Timeout is optional and defaults to 15 seconds.
The [`navigate`](#navigate-navigate) step has a `waitForNetworkIdle` option that can be set to `true` to get the same behavior.
```json
{
"action": "waitForNetworkIdle",
"timeout": 10000
}
```
### Wait for Condition (`waitForCondition`)
Waits for a custom JavaScript condition to become true. Useful for waiting on authentication cookies, localStorage values, or other complex conditions that can't be expressed with simple element selectors or URL patterns.
```json
{
"action": "waitForCondition",
"script": "document.cookie.includes('auth_token=')",
"timeout": 10000,
"interval": 250
}
```
## β‘οΈ Interaction Steps
### Click Element (`click`)
Clicks the element specified by the given selector on the page. The action automatically waits for the element to appear before clicking (default timeout: 10 seconds).
```json
{
"action": "click",
"selector": "#button"
}
```
With custom timeout:
```json
{
"action": "click",
"selector": "#button",
"timeout": 5000
}
```
### Type Text (`type`)
Types the given text into the element specified by the given selector on the page.
```json
{
"action": "type",
"selector": "#input",
"value": "Hello World"
}
```
### Select Dropdown (`dropdownSelect`)
Selects the given value from the dropdown specified by the given selector on the page. The selection happens based on the `value` attribute of the option.
```json
{
"action": "dropdownSelect",
"selector": "#dropdown",
"value": "Option 1"
}
```
### Run JavaScript (`runJs`)
Runs the given JavaScript in the page context. If a promise is returned, it will be awaited.
If you want to use the result of a script in subsequent steps, use the [extract](#extract-extract) step instead.
```json
{
"action": "runJs",
"script": "document.querySelector('#example').click();"
}
```
## β
Verification Steps
These steps are used inside `checkAuth` to verify if the user is authenticated.
### Check Element Exists (`checkElementExists`)
Checks if the given selector exists on the page. Typically used for authentication checks.
```json
{
"action": "checkElementExists",
"selector": "#example"
}
```
### Check URL (`checkURL`)
Checks if the current URL matches the given URL. Supports wildcard patterns like `https://example.com/dashboard/**`.
```json
{
"action": "checkURL",
"url": "https://example.com"
}
```
### Run JavaScript (`runJs`)
The `runJs` step can be used as verification step as well. By running a script that returns a truthy or falsy value, you can verify if the user is authenticated.
```json
{
"action": "runJs",
"script": "document.cookie.includes('authToken');"
}
```
#### Script formats
Scripts run in the browser page context (no Node APIs). You can use:
- **Expressions** that evaluate to a value
- **Function declarations or arrow functions** (sync or async)
- **IIFEs** for more complex logic
When a script is a function, Invoice Radar invokes it. Some steps pass arguments (for example `transform` and script pagination in `extractAll`).
```json
{
"action": "runJs",
"script": "document.title"
}
```
```json
{
"action": "runJs",
"script": "() => document.title"
}
```
```json
{
"action": "runJs",
"script": "(async () => { return await Promise.resolve('ok') })()"
}
```
## βοΈ Data Extraction Steps
These steps are used to load data from the page, like a list of items or a single value, and use it in subsequent steps.
### Extract (`extract`)
Extracts a single piece of data from the page and stores it in a variable.
**Using CSS fields:**
```json
{
"action": "extract",
"variable": "account",
"fields": {
"id": "#team-id",
"name": "#team-name",
"url": {
"selector": "#team-link",
"attribute": "href"
}
}
}
```
In this example `account` is used as variable name, and the fields `id`, `name`, and `url` are extracted using CSS selectors. They can be used in subsequent steps using the `{{account.id}}`, `{{account.name}}`, and `{{account.url}}` placeholders.
**Using JavaScript:**
```json
{
"action": "extract",
"variable": "token",
"script": "localStorage.getItem('authToken')"
}
```
This example creates a `token` variable that is extracted using JavaScript. The value can be accessed using the `{{token}}` placeholder. It's also possible to return an object.
### Extract All (`extractAll`)
Extracts a list of data from the page, and runs the given steps for each item. This is commonly used to iterate over a list of invoices and download them.
For each element matching the `selector`, the fields are extracted and stored in the `variable` available in the `forEach` steps.
**Good to know**:
- Each selector inside the `fields` object is automatically scoped to the matched element
- The `variable` field is optional. If not provided, the extracted data will be stored in the default variable `item`
- The current index can be accessed using the `{{index}}` placeholder. It starts at 0 and increments for each item
**With CSS fields:**
```json
{
"action": "extractAll",
"selector": ".invoice-list .invoice-item",
"variable": "invoice",
"fields": {
"id": "td.invoice-id",
"date": "td.invoice-date",
"total": "td.invoice-total",
"url": {
"selector": "a.invoice-link",
"attribute": "href"
}
},
"forEach": [
{
"action": "navigate",
"url": "{{invoice.url}}"
},
{
"action": "downloadPdf",
"invoice": "{{invoice}}"
}
]
}
```
**With JavaScript:**
When using JavaScript, the result should be an array of objects or values. If the result is a promise, it will be awaited.
```json
{
"action": "extractAll",
"script": "Array.from(document.querySelectorAll('#year-selector option')).map(option => option.value);",
"variable": "year",
"forEach": [
{
"action": "dropdownSelect",
"selector": "#year-selector",
"value": "{{year}}"
}
]
}
```
#### Transform
Use `transform` to turn the raw `extractAll` output into an array of items. The transform script receives the **full raw output** as its only argument and must return an array.
- **Selector-based extractAll**: the raw output is the extracted items array (after field extraction).
- **Script-based extractAll**: the raw output is whatever your script returns.
If you do not provide `transform`, script-based `extractAll` must return an array or an object with an `items` array.
```json
{
"action": "extractAll",
"script": "fetch('https://example.com/api/invoices').then(res => res.json())",
"transform": "(result) => result.items",
"variable": "invoice",
"forEach": [
{
"action": "downloadPdf",
"url": "{{invoice.pdfUrl}}",
"document": {
"id": "{{invoice.id}}",
"date": "{{invoice.date}}",
"total": "{{invoice.total}}"
}
}
]
}
```
#### Pagination
`extractAll` supports pagination via the `pagination` property. There are two modes:
- **Selector pagination**: click a "next" selector until it no longer exists.
- **Script pagination**: decide whether to continue based on the raw script result (script extractAll) or the extracted items array (selector extractAll).
Selector pagination works with both selector-based and script-based `extractAll`. Script pagination also works with selector-based `extractAll`.
**Selector pagination:**
```json
{
"action": "extractAll",
"selector": ".invoice-row",
"fields": {
"id": ".invoice-id",
"date": ".invoice-date",
"total": ".invoice-total",
"url": ".invoice-link[href]"
},
"pagination": {
"selector": "button.next-page",
"waitForSelector": ".invoice-row"
},
"forEach": [
{
"action": "navigate",
"url": "{{item.url}}"
}
]
}
```
If `waitForSelector` is omitted, Invoice Radar waits for network idle after clicking the next selector.
**Script pagination:**
```json
{
"action": "extractAll",
"script": "async (prev) => { const cursor = prev?.nextCursor; return fetch(`https://example.com/api/invoices?cursor=${cursor ?? ''}`).then(res => res.json()) }",
"transform": "(result) => result.items",
"pagination": {
"script": "(result) => Boolean(result.nextCursor)"
},
"forEach": [
{
"action": "downloadPdf",
"url": "{{item.pdfUrl}}",
"document": "{{item}}"
}
]
}
```
With script pagination:
- The `extractAll` script receives the **previous raw result** as its first argument (`prev`).
- For script-based `extractAll`, `pagination.script` receives the **current raw result**.
- For selector-based `extractAll`, `pagination.script` receives the **extracted items array** (after `transform`) and can also trigger pagination actions like clicking "next".
- Script pagination is not limited to APIs; it also works for in-page data, computed lists, or any scripted loop.
#### Pagination behavior
Use `pagination.behavior` to control when `forEach` runs:
- `processThenPaginate` (default): process items on each page, then paginate.
- `paginateThenProcess`: collect all pages first (with de-duplication), then process once.
Items are filtered against ignored document IDs and `startDate` before running `forEach`.
### Extract Network Response (`extractNetworkResponse`)
Extracts data from a network response by matching the URL path. The URL parameter is used as a substring match against network requests, so you only need to provide a unique part of the URL path.
The response content is automatically parsed as JSON if possible, otherwise returned as a string. You can optionally transform the response using the `transform` parameter.
```json
{
"action": "extractNetworkResponse",
"url": "api/invoices",
"variable": "response"
}
```
With transformation:
```json
{
"action": "extractNetworkResponse",
"url": "api/data",
"variable": "items",
"transform": "(response) => response.items.map(item => ({ id: item.id, date: item.createdAt, total: `${item.currency} ${item.amount}` }))"
}
```
The transformed data will be stored in the specified variable and can be used in subsequent steps.
## π Document Retrieval Steps
These steps are used to download documents and process them in Invoice Radar. All steps require the `document` object to be passed as an argument, which contains the metadata of the document.
The `document` argument has the following fields:
**Required:**
- `id`: The unique document ID (e.g., `INV-123` or `123456`)
- `date`: The invoice date as a string. Supports many common date formats:
- ISO format: `2022-01-01` or with time `2022-01-01T12:00:00.000Z`
- US format: `01/01/2022`
- EU format: `01.01.2022`
- Written format: `January 1, 2022`
- Unix timestamp: `1640995200000`
The date will be automatically parsed and normalized to ISO format internally.
**Recommended:**
- `total`: The invoice total amount including the currency (e.g., `$100.00` or `β¬100.00` or `100 EUR` or `100,00β¬`)
- The built-in parser will try to extract the amount and currency from the string
**Optional:**
- `currency`: Currency code or symbol that overrides any currency detected from the `total` field (e.g., `EUR`, `β¬`, `USD`, `$`, `GBP`, `Β£`)
- When provided, this field takes precedence over currency detected from the `total` field
- Supports both currency codes (EUR, USD, GBP) and symbols (β¬, $, Β£)
- `type`: The type of the document (defaults to `auto`)
- Can be set to `auto`, `invoice`, `outgoing_invoice`, `receipt`, `refund` or `other`
- `auto` will try to detect the type based on the document
- `metadata`: Additional metadata for the document, placed inside the `document` object:
```json
{
"action": "printPdf",
"document": {
"id": "{{invoice.id}}",
"date": "{{invoice.date}}",
"total": "{{invoice.total}}",
"metadata": {
"orderNumber": "{{invoice.orderNumber}}",
"vendorName": "Acme Inc"
}
}
}
```
**Deprecated:** Placing `metadata` at the step level (outside the `document` object) still works but is deprecated. Always place metadata inside the `document` object.
You can either pass every field separately or the whole object if it contains all required fields.
### Download PDF (`downloadPdf`)
Downloads a PDF from the given URL. Both relative and absolute URLs are supported.
```json
{
"action": "downloadPdf",
"url": "https://example.com/invoice.pdf",
"document": {
"id": "{{item.invoiceId}}",
"date": "{{item.date}}",
"total": "{{item.total}}"
}
}
```
You can optionally include custom HTTP headers with the request:
```json
{
"action": "downloadPdf",
"url": "https://example.com/invoice.pdf",
"headers": {
"Authorization": "Bearer {{token}}",
"X-Custom-Header": "value"
},
"document": {
"id": "{{item.invoiceId}}",
"date": "{{item.date}}",
"total": "{{item.total}}"
}
}
```
### Wait for PDF Download (`waitForPdfDownload`)
Waits for a PDF download. Timeout defaults to 15 seconds.
```json
{
"action": "waitForPdfDownload",
"timeout": 10000,
"document": {
"id": "{{item.invoiceId}}",
"date": "{{item.date}}",
"total": "{{item.total}}"
}
}
```
### Print Page as PDF (`printPdf`)
Prints the current page to a PDF file.
```json
{
"action": "printPdf",
"document": {
"id": "{{item.invoiceId}}",
"date": "{{item.date}}",
"total": "{{item.total}}"
}
}
```
**Example with currency override:**
```json
{
"action": "printPdf",
"document": {
"id": "{{item.invoiceId}}",
"date": "{{item.date}}",
"total": "{{item.amount}}", // Amount without currency (e.g., "100.50")
"currency": "EUR" // Explicit currency override
}
}
```
### Download Base64 PDF (`downloadBase64Pdf`)
Downloads a PDF from a base64 encoded string.
```json
{
"action": "downloadBase64Pdf",
"base64": "{{item.base64String}}",
"document": {
"id": "{{item.invoiceId}}",
"date": "{{item.date}}",
"total": "{{item.total}}"
}
}
```
## π Conditional Logic Steps
### If (`if`)
Runs the given steps if the condition is true. If the condition is false, the `else` steps are executed.
```json
{
"action": "if",
"script": "'{{invoice.url}}'.includes('pdf')",
"then": [
{
"action": "click",
"selector": "#example"
}
],
"else": [
{
"action": "navigate",
"url": "https://example.com/fallback"
}
]
}
```
## π¦ Miscellaneous Steps
### Sleep (`sleep`)
Waits for the given amount of time in milliseconds.
This is generally not recommended. In most cases, it's better to use the [waitForElement](#wait-for-element-waitforelement), [waitForURL](#wait-for-url-waitforurl) or [waitForNetworkIdle](#wait-for-network-idle-waitfornetworkidle) steps.
```json
{
"action": "sleep",
"duration": 1000
}
```
### Expose Option (`exposeOption`)
Exposes a configuration option to the user. The `config` field is the key of one of the options in the `configSchema`.
For example, if the `configSchema` is `{ "teamId": { "type": "string", "label": "Team ID" } }`, the `config` field can be `teamId`.
```json
{
"action": "exposeOption",
"config": "teamId",
"option": "{{option}}"
}
```
## βοΈ Snippets
Snippets are pre-built sets of steps that simplify common tasks. The steps for a specific snippet are visible inside the developer tools.
Currently, it's not possible to create custom snippets. If you have a common task that you think would be useful as a snippet, please create an issue on GitHub.
### Get Invoice from Stripe URL (`getInvoiceFromStripeUrl`)
Extracts an invoice from a Stripe invoice URL.
```json
{
"action": "runSnippet",
"snippet": "getInvoiceFromStripeUrl",
"args": {
"url": "https://invoice.stripe.com/i/inv_123"
}
}
```
### Get Invoices from Stripe Customer Portal (`getInvoicesFromStripeBillingPortal`)
Extracts available invoices from a Stripe billing portal.
```json
{
"action": "runSnippet",
"snippet": "getInvoicesFromStripeBillingPortal",
"args": {
"url": "https://stripe-portal.example.com/billing"
}
}
```
### Get Invoices from Chargebee Customer Portal (`getInvoicesFromChargebeePortal`)
Extracts available invoices from a Chargebee billing portal.
```json
{
"action": "runSnippet",
"snippet": "getInvoicesFromChargebeePortal",
"args": {
"url": "https://example.chargebee.com/portal/v2"
}
}
```
# BuchhaltungsButler (/docs/export/buchhaltungsbutler)
---
title: BuchhaltungsButler
description: Automatically upload invoices to BuchhaltungsButler as inbound receipts
---
## Overview
The BuchhaltungsButler integration automatically uploads your collected invoices to BuchhaltungsButler as inbound receipts (Eingangsbelege).
## Requirements
- A BuchhaltungsButler account
- API credentials: Client ID, Secret, and Key
## Setup
### Getting your API credentials
1. Log in to your BuchhaltungsButler account
2. Follow the official API setup guide
3. Generate your API Client ID, Secret, and Key
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **BuchhaltungsButler**
3. Enter your API credentials:
- API Client ID
- API Secret
- API Key
4. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
5. Enable the destination
## How it works
When you export invoices to BuchhaltungsButler:
- Each invoice is created as an **inbound receipt** (Eingangsbeleg)
- The PDF is attached automatically
- Metadata (vendor, date, amount) is included
### Rate limiting
BuchhaltungsButler's API is limited to **10 requests per minute**. Invoice Radar automatically throttles bulk exports to stay within these limits. Large exports may take longer due to this restriction.
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time. Note that large batches are automatically throttled to respect API limits.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Troubleshooting
**"Authentication failed" error**
- Verify all three credentials (Client ID, Secret, Key) are correct
- Make sure you copied them without extra spaces
- Check that your API access hasn't been revoked
**Exports taking a long time**
- This is normal for large batches due to rate limiting (10 requests/minute)
- A batch of 100 invoices takes approximately 10 minutes
**Invoices not appearing in BuchhaltungsButler**
- Check that the destination is enabled
- Verify invoices meet the start date criteria
- Look for error messages in the export history
## Security
- Your API credentials are encrypted and stored only on your device
- Invoices are sent directly to BuchhaltungsButler's API with no intermediary
- All connections use HTTPS/TLS encryption
---
Need help? Check the [Automatic Export overview](/docs/export) or our [Security & Privacy](/docs/security-privacy) guide.
# Email Forwarding (/docs/export/email)
---
title: Email Forwarding
description: Automatically forward invoices to any email address
---
## Overview
The Email Forwarding destination automatically sends your collected invoices to any email address. This is useful for email-based accounting tools, forwarding to your accountant, or integration with services that accept documents via email.
## Requirements
- A valid email address to receive invoices
## Setup
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **Email**
3. Enter the recipient email address
4. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
5. Enable the destination
## How it works
When you export invoices via email:
- Each invoice is sent as an email with the PDF attached
- The email subject includes the vendor name and invoice ID
- The email body contains invoice metadata (date, amount, etc.)
## Use cases
- **Accountant forwarding** - Automatically send invoices to your accountant's inbox
- **Email-based accounting** - Services that import documents from email
- **Backup** - Keep a copy of all invoices in a dedicated email folder
- **Team sharing** - Forward invoices to a shared team inbox
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Privacy note
Unlike other export destinations, email exports are sent through Invoice Radar's server:
- Your invoices are forwarded through our email service
- **PDFs are not stored** - they are forwarded immediately and discarded
- We do not read or analyze invoice contents
- This is necessary because sending emails requires a mail server
All other destinations (sevdesk, Lexware, Paperless-ngx, Folder, Webhook, etc.) send data directly from your device to the service.
## Troubleshooting
**Emails not arriving**
- Check your spam/junk folder
- Verify the email address is correct
- Some email providers may block automated emails
**Delayed delivery**
- Email delivery can take a few minutes
- Check the export status in Invoice Radar for confirmation
**Emails marked as spam**
- Add our sending address to your contacts or safe senders list
- Check your email provider's spam settings
## Security
- Your recipient email address is encrypted and stored only on your device
- Email connections use TLS encryption
- PDFs are forwarded through our server but not stored
---
Need help? Check the [Automatic Export overview](/docs/export) or our [Security & Privacy](/docs/security-privacy) guide.
# Folder Export (/docs/export/folder)
---
title: Folder Export
description: Automatically save invoices to a local folder with customizable organization
---
## Overview
The Folder export destination automatically saves your collected invoices to a folder on your computer. This is perfect for local backup, integration with cloud storage, or feeding into other document management systems.
## Requirements
- A destination folder on your computer
- Write permissions to that folder
## Setup
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **Folder**
3. Click **Choose Folder** to select your destination folder
4. Configure your export pattern (optional):
- **Same as one-time exports** - Uses your global export pattern
- **Custom pattern** - Create a unique pattern for this destination
5. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
6. Enable the destination
## How it works
When you export invoices to a folder:
- Each invoice PDF is saved to your chosen folder
- Files are organized according to your export pattern
- Existing files are not overwritten (duplicates are tracked)
### Export patterns
Patterns let you organize invoices into subfolders with custom naming. For example:
| Pattern | Result |
|---------|--------|
| `{{vendor}}` / `{{invoiceId}}` | `Acme Corp/INV-2025-001.pdf` |
| `{{year}}/{{month}}` / `{{vendor}}-{{invoiceId}}` | `2025/03/Acme Corp-INV-2025-001.pdf` |
See the [Patterns guide](/docs/export/patterns) for all available placeholders and examples.
## Cloud storage integration
The Folder destination works great with cloud storage services that sync local folders:
- **Google Drive** - Save to your Google Drive folder
- **Dropbox** - Save to your Dropbox folder
- **OneDrive** - Save to your OneDrive folder
- **iCloud Drive** - Save to your iCloud Drive folder
- **Synology Drive** - Save to your Synology sync folder
Simply choose your cloud storage's local sync folder as the destination.
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Troubleshooting
**"Permission denied" error**
- Verify you have write access to the destination folder
- On macOS, check System Preferences β Security & Privacy β Files and Folders
**Files not appearing**
- Check that the destination is enabled
- Verify invoices meet the start date criteria
- Look for error messages in the export history
**Wrong folder structure**
- Review your export pattern settings
- Use the pattern preview to see how files will be organized
**Folder access lost after restart**
- On macOS, you may need to re-grant folder access after system updates
- Try selecting the folder again in settings
## Security
- Files are saved directly to your local filesystem
- No data is sent to external servers
- Your folder path is stored only on your device
---
Need help? Check the [Automatic Export overview](/docs/export) or the [Patterns guide](/docs/export/patterns).
# Overview (/docs/export)
---
title: Overview
description: Forward your collected invoices to accounting software and external services
---
export const IntegrationCard = ({ href, logo, icon, title, description }) => {
const content = (
<>
{logo &&
}
{icon && (
{icon}
)}
>
);
return href ? (
{content}
) : ({content}
); };
## One-Time Export
Use the **Export** button in Invoice Radar to save invoices directly to a folder on your computer. No configuration required.
**Options:**
- **Time period** - Export all invoices or filter by date range
- **Export structure** - Choose between a flat folder or organized subfolders using your [pattern](/docs/export/patterns)
- **Include CSV** - Add a `documents.csv` file containing metadata for all exported invoices (vendor, date, amount, currency, invoice ID)
The CSV option is useful for importing invoice data into spreadsheets, accounting software, or custom workflows.
## Export Destinations
Export destinations let you connect Invoice Radar to external services. Once configured, you can sync invoices with a single click - Invoice Radar tracks what's been exported to avoid duplicates.
**How it works:**
1. Configure a destination in **Settings β Export**
2. Click **Export Now** to sync all unsynced invoices
3. Or export individual invoices from the document details panel
This gives you time to review, delete, or enrich data before sending to external services.
## Supported Destinations
### Third-Party Services
### Other Destinations
## Getting Started
1. Navigate to **Settings β Export** in Invoice Radar
2. Click **Add destination** and select your service
3. Enter your credentials or configuration
4. Choose a start date for which invoices to include
5. Enable the destination
See individual destination pages for detailed setup instructions.
## File Organization
When exporting to destinations that support file organization (like Folder), you can customize how invoices are named and organized using patterns.
See the [Patterns guide](/docs/export/patterns) for available placeholders and examples.
## Security & Privacy
- **Stored locally**: API keys and credentials are encrypted and stored only on your device
- **Direct connections**: All destinations except email connect directly to the service API
- **Email forwarding**: Sent through Invoice Radar's server, but PDFs are not stored
- **HTTPS/TLS**: All API calls use secure encryption
- **Multi-device sync**: Credentials sync via end-to-end encrypted collaboration
See our [Security & Privacy](/docs/security-privacy) guide for more details.
# Lexware (/docs/export/lexware)
---
title: Lexware
description: Automatically upload invoices to Lexware with intelligent document type mapping
---
## Overview
The Lexware integration automatically uploads your collected invoices to Lexware and creates vouchers with intelligent document type mapping based on the invoice type.
## Requirements
- A Lexware account with API access
- Your Lexware API key
## Setup
### Getting your API key
1. Log in to your Lexware account
2. Go to the Lexware Add-ons page
3. Generate or copy your API key
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **Lexware**
3. Paste your API key
4. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
5. Enable the destination
## How it works
When you export invoices to Lexware, Invoice Radar automatically maps document types:
| Invoice Radar Type | Lexware Voucher Type |
|--------------------|----------------------|
| Invoice | Purchase invoice (Eingangsrechnung) |
| Outgoing invoice | Sales invoice (Ausgangsrechnung) |
| Refund | Purchase credit note (Gutschrift) |
Each voucher includes:
- Vendor name
- Invoice date
- Amount and currency
- Attached PDF document
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Troubleshooting
**"Invalid API key" error**
- Verify your API key is correct
- Check that your Lexware subscription includes API access
**Wrong document type**
- Document type mapping is based on how Invoice Radar categorizes the invoice
- You can adjust the invoice type in Invoice Radar before exporting
**Invoices not appearing in Lexware**
- Check that the destination is enabled
- Verify invoices meet the start date criteria
- Look for error messages in the export history
## Security
- Your API key is encrypted and stored only on your device
- Invoices are sent directly to Lexware's API with no intermediary
- All connections use HTTPS/TLS encryption
---
Need help? Check the [Automatic Export overview](/docs/export) or our [Security & Privacy](/docs/security-privacy) guide.
# Paperless-ngx (/docs/export/paperless-ngx)
---
title: Paperless-ngx
description: Automatically upload invoices to your self-hosted Paperless-ngx instance
---
## Overview
The Paperless-ngx integration automatically uploads your collected invoices to your self-hosted Paperless-ngx document management system.
## Requirements
- A running Paperless-ngx instance (self-hosted)
- Your instance URL (accessible from your device)
- Your Paperless-ngx API token
## Setup
### Getting your API token
1. Log in to your Paperless-ngx instance
2. Go to **My Profile**
3. Find the **API** section
4. Copy your API token
For detailed instructions, see the official Paperless-ngx API documentation.
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **Paperless-ngx**
3. Enter your configuration:
- **Instance URL** - Your Paperless-ngx server address (e.g., `https://paperless.example.com`)
- **API Token** - Your Paperless-ngx API token
4. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
5. Enable the destination
## How it works
When you export invoices to Paperless-ngx:
- Each invoice is uploaded as a new document
- The document title is set to: `{Vendor Name} - {Invoice ID}`
- The invoice date is preserved as the document date
- The PDF is uploaded and processed by Paperless-ngx's OCR
Paperless-ngx will then apply its matching rules, tags, and correspondent assignments based on your configuration.
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Network requirements
Your Paperless-ngx instance must be accessible from the device running Invoice Radar:
- **Local network**: Use your local IP or hostname
- **Remote access**: Ensure your instance is accessible via a public URL or VPN
- **HTTPS recommended**: Use TLS encryption for security
## Troubleshooting
**"Connection refused" error**
- Verify your instance URL is correct and accessible
- Check that your Paperless-ngx server is running
- Ensure no firewall is blocking the connection
**"Unauthorized" error**
- Verify your API token is correct
- Check that your token hasn't expired or been regenerated
- Make sure your user has permission to upload documents
**Documents not appearing in Paperless-ngx**
- Check the Paperless-ngx logs for processing errors
- Verify the upload succeeded in Invoice Radar's export history
- Documents may take time to process through OCR
**SSL/TLS errors**
- If using a self-signed certificate, you may need to configure your system to trust it
- Consider using a valid SSL certificate (e.g., from Let's Encrypt)
## Security
- Your API token is encrypted and stored only on your device
- Invoices are sent directly to your Paperless-ngx instance with no intermediary
- All connections should use HTTPS/TLS encryption
---
Need help? Check the [Automatic Export overview](/docs/export) or our [Security & Privacy](/docs/security-privacy) guide.
# Patterns (/docs/export/patterns)
---
title: Patterns
description: Customize how your invoices are organized when exported
icon: folder-open
---
## What are export patterns?
Export patterns let you organize your exported invoices into custom folder structures and filenames using placeholders. This helps you maintain a consistent file organization across your exports.
## How patterns work
Patterns consist of two parts:
- **Folder pattern**: Defines the folder structure (subfolder hierarchy)
- **Filename pattern**: Defines how files are named
Example:
- **Folder pattern**: `{{year}}/{{month}}`
- **Filename pattern**: `{{vendor}}-{{invoiceId}}`
- **Result**: Files like `2025/03/Acme Corp-INV-2025-001.pdf`
## Available placeholders
You can use these placeholders in your patterns:
| Placeholder | Example | Description |
| --------------- | ------------ | ------------------------------------ |
| `{{vendor}}` | Acme Corp | Company name from the invoice |
| `{{invoiceId}}` | INV-2025-001 | Invoice identifier |
| `{{year}}` | 2025 | Year (4 digits) |
| `{{month}}` | 03 | Month (2 digits, zero-padded) |
| `{{day}}` | 15 | Day of month (2 digits, zero-padded) |
| `{{currency}}` | EUR | Currency code |
| `{{quarter}}` | Q1 | Quarter (Q1, Q2, Q3, Q4) |
## Preset patterns
Invoice Radar comes with pre-configured patterns you can use as a starting point:
### Default
- **Folder pattern**: `{{vendor}}`
- **Filename pattern**: `{{invoiceId}}`
- **Result**: Organized by company name
### By Year & Month
- **Folder pattern**: `{{year}}/{{month}}`
- **Filename pattern**: `{{vendor}}-{{invoiceId}}`
- **Result**: Organized by date, with vendor and ID in filename
### By Quarter
- **Folder pattern**: `{{quarter}}`
- **Filename pattern**: `{{vendor}}-{{invoiceId}}`
- **Result**: Organized by quarter within the year
### Flat Structure
- **Folder pattern**: (empty)
- **Filename pattern**: `{{vendor}}-{{invoiceId}}`
- **Result**: All files in one folder, named with vendor and ID
## Using patterns
### In one-time exports
1. Go to **Settings β Export Pattern**
2. Click a preset pattern or customize your own
3. See a live preview of how your files will be organized
4. Click **Save Export Pattern** when satisfied
5. When you export documents via one-time export, they'll use this pattern
### In Folder destinations
The Folder destination offers two options:
- **Same as one-time exports**: Uses your saved pattern from Settings
- **Custom pattern**: Create a different pattern just for this destination
This lets you organize different destinations differently. For example:
- One-time exports by vendor
- Folder destination by date
- Email destination uses a flat structure
## Examples in practice
Given an invoice from "Acme Corp" with ID "INV-2025-001" dated March 15, 2025:
| Pattern | Result |
| ------------------------------------------------------- | ------------------------------------ |
| `{{vendor}}` / `{{invoiceId}}` | `Acme Corp/INV-2025-001.pdf` |
| `{{year}}/{{month}}` / `{{vendor}}-{{invoiceId}}` | `2025/03/Acme Corp-INV-2025-001.pdf` |
| `{{quarter}}` / `{{invoiceId}}` | `Q1/INV-2025-001.pdf` |
| (empty) / `{{year}}-{{month}}-{{vendor}}-{{invoiceId}}` | `2025-03-Acme Corp-INV-2025-001.pdf` |
## Valid characters
Patterns are converted to valid filenames/folder names. Invalid characters (like `/`, `\\`, `:`, `?`, etc.) are handled as follows:
- The folder separator `/` creates subdirectories
- Other invalid characters are replaced with spaces
- Leading/trailing spaces are removed
- Multiple consecutive spaces are collapsed
**Example**: An invoice from "A/B Corp" with pattern `{{vendor}}/receipt` becomes `A B Corp/receipt.pdf`
# sevdesk (/docs/export/sevdesk)
---
title: sevdesk
description: Automatically upload invoices to sevdesk as vouchers with full metadata
---
## Overview
The sevdesk integration automatically uploads your collected invoices to sevdesk as vouchers. Each invoice is created with full metadata including vendor name, date, and amount, with the PDF automatically attached.
## Requirements
- A sevdesk account
- Your sevdesk API token
## Setup
### Getting your API token
1. Log in to your sevdesk account
2. Go to **Settings β Users β Your User β API Token**
3. Copy your API token
For detailed instructions, see the official sevdesk guide.
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **sevdesk**
3. Paste your API token
4. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
5. Enable the destination
## How it works
When you export invoices to sevdesk:
- Each invoice is created as a **voucher** (Beleg) in sevdesk
- The PDF is attached to the voucher automatically
- Metadata is mapped:
- Vendor name β Supplier
- Invoice date β Voucher date
- Amount β Voucher amount
- Currency β Voucher currency
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Troubleshooting
**"Invalid API token" error**
- Verify your API token is correct and hasn't been regenerated
- Make sure you copied the full token without extra spaces
**Invoices not appearing in sevdesk**
- Check that the destination is enabled
- Verify invoices meet the start date criteria
- Look for error messages in the export history
**Duplicate vouchers**
- Invoice Radar tracks exports to prevent duplicates
- If you see duplicates, check if multiple sevdesk destinations are configured
## Security
- Your API token is encrypted and stored only on your device
- Invoices are sent directly to sevdesk's API with no intermediary
- All connections use HTTPS/TLS encryption
---
Need help? Check the [Automatic Export overview](/docs/export) or our [Security & Privacy](/docs/security-privacy) guide.
# Webhook (/docs/export/webhook)
---
title: Webhook
description: Send invoice data to any HTTP endpoint for custom integrations
---
## Overview
The Webhook integration sends your invoice data to any HTTP endpoint. This is perfect for custom integrations, automation workflows, or connecting to services not directly supported by Invoice Radar.
## Requirements
- An HTTP endpoint that accepts POST requests
- The endpoint must be publicly accessible (or accessible from your device)
## Setup
### Configuring in Invoice Radar
1. Open Invoice Radar and go to **Settings β Export**
2. Click **Add destination** and select **Webhook**
3. Enter your configuration:
- **Webhook URL** - Your HTTP endpoint (must be HTTPS)
- **Custom Headers** (optional) - Authentication headers, API keys, etc.
4. Choose a start date:
- **All invoices** - Export everything in your collection
- **From specific date** - Only export invoices after the selected date
5. Click **Test Webhook** to validate your endpoint
6. Enable the destination
## Request format
Invoice Radar sends a POST request to your webhook URL for each invoice:
### Headers
```
Content-Type: application/json
```
Plus any custom headers you configure (e.g., `Authorization`, `X-API-Key`).
### Body
```json
{
"type": "invoice",
"amount": 1499.99,
"currency": "EUR",
"id": "INV-2025-001",
"vendor": "Example Vendor",
"date": "2025-11-12",
"pdfBase64": "JVBERi0xLjQK..."
}
```
| Field | Type | Description |
|-------|------|-------------|
| `type` | string | Document type (`invoice`, `refund`, etc.) |
| `amount` | number | Invoice amount |
| `currency` | string | Currency code (e.g., `EUR`, `USD`) |
| `id` | string | Invoice identifier |
| `vendor` | string | Vendor/company name |
| `date` | string | Invoice date (YYYY-MM-DD format) |
| `pdfBase64` | string | PDF file as base64-encoded string |
## Response handling
Your endpoint should respond with an HTTP status code:
| Status | Meaning |
|--------|---------|
| 200-299 | Success - invoice marked as exported |
| 4xx | Client error - invoice marked as failed |
| 5xx | Server error - invoice marked as failed, will retry |
| Timeout | After 30 seconds - invoice marked as failed, will retry |
Failed invoices are automatically retried during future exports.
## Use cases
### Automation platforms
Connect to automation tools like:
- Zapier - Use Webhooks by Zapier trigger
- n8n - Use the Webhook node
- Make (formerly Integromat) - Use Custom Webhook trigger
- Pipedream - Use HTTP trigger
### Custom business logic
- Store invoices in your own database
- Trigger internal workflows
- Send to unsupported accounting systems
- Custom data processing and enrichment
### Internal systems
- Forward to internal APIs
- Integration with ERP systems
- Custom document management
## Example implementations
### n8n workflow
1. Create a new workflow with a **Webhook** node
2. Copy the webhook URL to Invoice Radar
3. Add processing nodes (e.g., decode base64, save file, update database)
4. Activate the workflow
### Simple Node.js server
```javascript
const express = require('express');
const app = express();
app.use(express.json({ limit: '50mb' }));
app.post('/invoice-webhook', (req, res) => {
const { vendor, id, amount, currency, date, pdfBase64 } = req.body;
console.log(`Received invoice ${id} from ${vendor}`);
console.log(`Amount: ${amount} ${currency}, Date: ${date}`);
// Decode and save PDF
const pdfBuffer = Buffer.from(pdfBase64, 'base64');
// ... save to disk or process further
res.status(200).json({ received: true });
});
app.listen(3000);
```
## Exporting invoices
### Bulk export
Click **Export Now** in the Export settings to sync all unsynced invoices. Progress is shown in real-time.
### Individual export
Open any invoice in the document details panel to see its export status and manually trigger an export.
## Troubleshooting
**"Test failed" error**
- Verify your webhook URL is correct and publicly accessible
- Check that your endpoint accepts POST requests
- Verify any authentication headers are correct
**Connection timeout**
- Ensure your endpoint responds within 30 seconds
- Check that your server is running and accessible
- Verify no firewall is blocking the connection
**Authentication errors**
- Double-check custom header names and values
- Ensure API keys or tokens haven't expired
**Large invoices failing**
- The request body can be large due to base64-encoded PDFs
- Ensure your server accepts payloads up to 50MB
- Check your server's request size limits
**Webhook returning 4xx/5xx errors**
- Check your server logs for the incoming request
- Verify your endpoint logic is correct
- Ensure your endpoint returns a 2xx status code on success
## Security
- Your webhook URL and headers are encrypted and stored only on your device
- Requests are sent directly from your device to your endpoint
- All connections use HTTPS/TLS encryption
- Consider using authentication headers (API keys, tokens) to secure your endpoint
---
Need help? Check the [Automatic Export overview](/docs/export) or our [Security & Privacy](/docs/security-privacy) guide.