Skip to main content

edf-tool

Your monthly export-of-services paperwork in one command, entirely on your own computer.

If you're an Indian freelancer or sole proprietor paid from abroad (directly or through Deel, Remote, an EOR and so on), your bank wants an EDF (Export Declaration Form) for each foreign payment. edf generates, every month:

File What it is
…_INV-2026-10.pdf Your invoice to the client: GSTIN, PAN, SAC, IGST line, place of supply, amount in words
…_EDF_INV-2026-10.xlsx The bank's own EDF workbook, filled in. The bank's formatting, dropdowns and logos are left untouched.
…_EDF_Form_INV-2026-10.pdf Two pages ready to sign and send: the Request Letter and the Invoice Details

Sample invoice (fake data)   Sample HDFC EDF request letter (fake data)

Generated from the fake data in config.example.json.


🔒 Privacy: nothing leaves your machine

  • No network access, ever. The code has no networking, sends no telemetry and doesn't check for updates. The test suite fails if any networking module is even imported.
  • Your details (PAN, GSTIN, bank account, client) are stored only in ~/.config/edf-tool/config.json, readable only by you (0600).
  • Your signature is stored only in ~/.config/edf-tool/signature.png.
  • Generated files go to ~/Documents/EDF/<invoice no>/, or wherever you choose.
  • No Python packages are needed: it's pure standard library, so there's no third-party code to trust.
  • PDFs are made locally. The invoice is drawn by edf itself, and the bank form PDF uses your own LibreOffice, run offline.

Install

Homebrew (macOS / Linux)

brew install krushiraj/tap/edf-tool

uv: run it without installing, or install it as a tool

uvx edf-tool --help           # one-off run
uv tool install edf-tool      # then just: edf

pipx / pip

pipx install edf-tool
# or: python3 -m pip install --user edf-tool

Straight from GitHub (no PyPI needed)

uvx --from git+https://github.com/krushiraj/edf-tool edf --help

Optional: LibreOffice, used only to turn the bank workbook into a PDF. The invoice PDF and the Excel file work without it.

brew install --cask libreoffice          # macOS
sudo apt install libreoffice-calc        # Debian/Ubuntu

You need Python 3.9 or newer; macOS already includes it. Check everything with edf doctor.


First run (one-time setup)

edf init

Setup asks for everything the bank form and the invoice need. Every required field must be answered and is checked as you type: PAN format, GSTIN format and check digit, GSTIN-matches-PAN, IFSC, SAC code, phone and email. It covers:

  1. You: business name, proprietor, address (as on your GST certificate), phone, email, PAN, GSTIN, IEC (or "Not applicable")
  2. Your bank account: branch, branch address, IFSC, account number
  3. Your client: legal name, address, country, registration/VAT number (optional)
  4. Who sends the money: the client directly, or a third party like Deel (name, address, relationship)
  5. The service: RBI purpose code (e.g. P0802), SAC code (e.g. 998314), description, scope
  6. Monthly defaults: usual amount, usual transfer fee, invoice number prefix
  7. (optional) A picture of your signature

If you run edf generate before setting up, it starts setup for you.

Prefer a file? Copy config.example.json, fill it in, then:

edf init --from my-details.json --signature ~/Desktop/signature.jpg
rm my-details.json        # the details now live in ~/.config/edf-tool/

Every month

edf generate
Monthly invoice + EDF   (Enter accepts the [suggested] value)

  Invoice date (DD-MM-YYYY) [30-09-2026]:
  Invoice number [INV-2026-10]:
  Work period start [31-08-2026]:
  Work period end [30-09-2026]:
  Due date [30-09-2026]:
  Invoice amount (USD) [2400.00]:
  Fee deducted before it reaches the bank [6.00]:
  Date on the bank request letter [07-10-2026]:

  Invoice   INV-2026-10  dated 30-09-2026, due 30-09-2026
  Client    Example Corp LLC
  Period    August 31, 2026 to September 30, 2026
  Amount    USD 2,400.00   fee USD 6.00   net to bank USD 2,394.00
  Generate these files? (yes/no) [yes]:

✓ Files in ~/Documents/EDF/INV-2026-10

Press Enter to accept each suggestion:

  • Invoice number: the next number after the last one you made.
  • Invoice date: the last day of last month (or of this month, if it's after the 15th).
  • Work period: from the last day of the previous month to the invoice date.

edf won't let you reuse an invoice number unless you add --force.

About the fee: if Deel, or a correspondent bank, takes a fee before the money reaches you, enter it here. The bank form then declares the full export value (the invoice amount) and the lower Net Realisable Value, and explains the difference in the remarks. That's what lets your bank close the EDF cleanly when slightly less money arrives.

Signing

Pick whichever suits you:

  • macOS Preview (or any PDF app): open the two PDFs and use Tools → Annotate → Signature.
  • No PDF signing app? Save your signature once, and edf will stamp it for you:
    edf signature set ~/Desktop/my-signature.jpg   # background removed automatically
    edf generate --sign                            # also writes *_signed.pdf copies
    edf sign INV-2026-10                           # or sign one you've already generated
    
    Use a photo or scan of your signature on white paper. PNG works everywhere; on a Mac, JPG and HEIC work too.
  • In the Excel file: once a signature is saved, it's placed on the signatory line automatically. You can turn this off with edf config set output.embed_signature_in_excel false.

Then send the bank form PDF and the invoice PDF to your branch, along with the .xlsx if they ask for it. Many branches also want your firm's stamp/seal next to the signature.


Commands

Command What it does
edf init First-run setup. --from FILE reads a JSON file, --force redoes setup, --signature IMG saves a signature
edf generate (gen) This month's files. Flags: --invoice-date, --invoice-no, --amount, --fee, --period-start/--period-end, --due-date, --letter-date, --sign, --dry-run, -y, --json
edf sign INV-… Signed PDFs for an invoice already generated
edf signature set IMG | show | remove Manage your saved signature
edf config show | path | edit | validate Look at, open or check the config
edf config set FIELD VALUE Change one value, e.g. edf config set defaults.amount 2500
edf history Invoices generated so far
edf doctor Checks the config, bank template, signature, LibreOffice and output folder
edf banks Supported bank forms
edf man The full manual (also man edf when installed with Homebrew)

Every command accepts --json for machine-readable output, and has --help with examples.

Exit codes:

  • 0 OK
  • 2 config missing or invalid
  • 3 bad input (date, amount, or an invoice number already used)
  • 4 bank form doesn't match the known version
  • 5 files written, but an optional step was skipped (e.g. no LibreOffice)
  • 130 cancelled

Configuration reference

The config lives in ~/.config/edf-tool/config.json; run edf config path to see the exact location. Edit it with edf config edit or edf config set.

Field Example Notes
exporter.firm_name ACME DEV STUDIO As on your GST certificate
exporter.proprietor Asha Kumar Signs the forms
exporter.constitution Sole Proprietorship
exporter.address_lines ["12 MG Road", "Bengaluru, KA 560038", "India"] Your GST-registered address
exporter.phone, exporter.email Printed on the invoice and the bank form
exporter.pan, exporter.gstin ABCDE1234F, 29ABCDE1234F1ZW Validated, including the GSTIN check digit
exporter.iec Not applicable Most service exporters don't have one
bank.branch_name, bank.branch_address, bank.ifsc From your bank statement
bank.account_no, bank.fee_account_no The account credited, and the account charges are debited from
client.name, client.address_lines, client.country The service recipient
client.short_address Example Corp LLC, San Francisco, CA, USA Used in narrow boxes
client.registration_number, client.vat_id Optional
third_party.enabled true Set to true if Deel, Remote or similar sends the money
third_party.name, .address, .short_address, .country, .relationship Deel, Inc. The bank must know about third-party payers (FEMA)
service.purpose_code, .purpose_description P0802, Software consulting / implementation RBI purpose code
service.sac 998314 998314 IT design & development; 998313 IT consulting & support
service.description, .short_description, .line_item, .scope Wording on the invoice and the form
service.currency USD Any 3-letter code
defaults.amount, defaults.payment_fee 2400, 6 Used as the suggestions each month
defaults.invoice_prefix INV-
output.folder ~/Documents/EDF
output.file_prefix Acme_Dev
output.embed_signature_in_excel true
bank_format hdfc_services See edf banks
bank_template (unset) Optional path to a newer copy of the same bank form

More than one client? Keep a separate profile for each one with EDF_TOOL_HOME:

EDF_TOOL_HOME=~/.config/edf-tool-clientB edf init
EDF_TOOL_HOME=~/.config/edf-tool-clientB edf generate

Using it with AI agents

edf is built to be driven by coding agents (Claude Code, Codex, Cursor and so on) without anything leaving your machine:

  • Everything works without prompts: -y, --from, and --json on every command, with documented exit codes.
  • AGENTS.md has step-by-step setup and monthly instructions, plus guardrails.
  • skills/edf-tool/SKILL.md is a ready-made agent skill. Copy it into your agent's skills folder, e.g. ~/.claude/skills/edf-tool/.

Tell your agent: "Set up edf-tool from github.com/krushiraj/edf-tool and generate this month's EDF."


Good to know

  • GST / LUT: the invoice shows IGST (0%). Exporting services without charging IGST requires a Letter of Undertaking (LUT), filed for free on the GST portal (form RFD-11). Without an LUT, IGST is payable and then refunded. Check with your CA. You can change the tax line's wording with edf config set defaults.igst_label "…".
  • IEC: usually not required for exporting services; the form says "Not applicable".
  • The bank form version: before filling anything, edf checks that the form's labels match the version it knows. If HDFC changes the form, edf stops with exit status 4 rather than writing into the wrong cells. Please open an issue, and remove your personal data from anything you attach.
  • Fees and realisation: keep Deel's (or your bank's) statement showing the fee. Banks sometimes ask for it when they close the EDF.

Troubleshooting

Problem Fix
No config found Run edf init
EDF form PDF skipped (exit status 5) Install LibreOffice, or open the .xlsx and export the REQUEST LETTER and Invoice Details 1 sheets as PDF
LibreOffice installed but not found export SOFFICE_PATH=/path/to/soffice
Signature has a grey box around it edf signature set img.jpg --threshold 170 (a lower value removes more background)
An invoice number was already used Use the next number, or add --force to regenerate it
Text cut off in a box on the bank form Shorten client.short_address, service.short_description or third_party.short_address

Contributing

Bug reports and new bank formats are welcome; see CONTRIBUTING.md. The tests run with only fake data: python3 -m unittest discover -s tests.

License

MIT, © Krushi Raj Tula. The bundled HDFC form belongs to HDFC Bank. It's included unchanged apart from removed document metadata, and isn't covered by the MIT license. This project is not affiliated with or endorsed by HDFC Bank.

Metadata

Release files for edf-tool 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for edf-tool 0.1.0
File Size Uploaded
edf_tool-0.1.0.tar.gz 327.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for edf-tool 0.1.0
File Interpreter ABI Platform
edf_tool-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 414.5 kB

Release files / edf_tool-0.1.0.tar.gz

Download URL edf_tool-0.1.0.tar.gz
Size 327.1 kB
Tags Source
SHA-256 checksum
How to use checksums
3a3adbeb5c8baef16161980e2d69b346644831612293ffcaaddfe1d81a05a123
BLAKE2b-256 checksum
How to use checksums
6a2c15c3260e5f869d5423c8f4f55a7bc44c65f133ff130ab1536506779f85d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / edf_tool-0.1.0-py3-none-any.whl

Download URL edf_tool-0.1.0-py3-none-any.whl
Size 87.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
45de389fbb4d3a49d2e0673a181279e0fbdccb3a368479999a6ec16bb360311c
BLAKE2b-256 checksum
How to use checksums
21ac785f7ba0284d68db51d2c91b8548d8d90d8825d40c703dcd6a9e1b81ef92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page