# 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
- **๐ Privacy & Security First** - All processing happens locally on your computer. Your credentials never leave your device.
- **๐ค Automated Collection** - Downloads invoices from web portals and scans email inboxes with intelligent document recognition.
- **๐ Extensible Platform** - Support for 100+ services out of the box, with a plugin system for any web portal.
## 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
- **[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 Gmail integration
---
## How it works
Invoice Radar scans your email attachments to find invoices. When you approve an invoice from a sender for the first time, it will automatically collect future invoices from that same sender. You stay in control of which companies you trust.
## Connecting your email
#### 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
You can use the Gmail login 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.
Note: The Gmail integration hasn't been reviewed by
Google yet, so you might see a warning during OAuth. This is normal for
applications in development.
#### Login with Microsoft
You can connect your Microsoft Outlook 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 Gmail 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)
# Invoices from Web Portals (/docs/invoices-from-web-portals)
---
title: Invoices from Web Portals
description: Automatically collect invoices from web portals using Invoice Radar
---
## Overview
Invoice Radar automates invoice collection from web portals by acting as your personal browser assistant. It navigates to your service providers, logs in, finds your invoices, and downloads them automatically - all running locally on your computer, just like a regular browser would.
## 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.
## Control over your credentials
You have complete control over how Invoice Radar handles your credentials:
### Manual Login (Maximum Security)
Log in manually each time you collect invoices. Invoice Radar never touches your credentials - it's exactly like using a regular browser.
### Stored Credentials (Balanced)
Let Invoice Radar securely store and autofill your credentials. All data is encrypted and stored locally in the safe storage on your device.
### Full Automation (Maximum Convenience)
Optionally store your two-factor authentication (OTP) secrets for completely hands-free operation.
## Supported Services
Invoice Radar supports a wide range of services including:
- Cloud providers (AWS, Google Cloud, Azure)
- Payment processors (Stripe, PayPal)
- SaaS platforms (Shopify, GitHub)
- Utility providers
- And many more...
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
---
## 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.
## 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"
}
```
#### 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",
"timeout": 120000
}
```
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.
### 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:
```json
{
"action": "navigate",
"url": "https://example.com/team/{{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",
"timeout": 120000
}
],
"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",
"timeout": 120000
}
]
```
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 `{{configKey}}`
- **Extracted data**: Use data from `extract` or `extractAll` steps
- **Built-in variables**: Like `{{index}}` in loops
### 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/{{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.
```json
{
"action": "click",
"selector": "#button"
}
```
### 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');"
}
```
## โ๏ธ 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}}"
}
]
}
```
### 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 (e.g., `{ "orderNumber": "12345" }`)
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.
```json
{
"action": "downloadPdf",
"url": "https://example.com/invoice.pdf",
"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"
}
}
```