Skip to content

Add Attachments To Invoice Emails - #70

Merged
dinoconstantinou87 merged 1 commit into
mainfrom
feat/invoice-email-attachments
Oct 15, 2026
Merged

dinoconstantinou87 merged 1 commit into
mainfrom
feat/invoice-email-attachments

Conversation

@dinoconstantinou87

Copy link
Copy Markdown
Owner

Summary

FreeAgent takes an attachments array on send_email, but the spec didn't declare it, so an invoice could only be emailed bare. The spec now declares it and invoice send-email takes --attachment.

$ freeagent invoice send-email 795071 --to client@example.com --from you@company.com --attachment timesheet.pdf --attachment receipt.png
  • --attachment <path> attaches a pdf, png, jpg, jpeg or gif file of up to 5MB. It can be repeated, as the API takes several files.
  • A bad file sends nothing. An unsupported type or an unreadable path is a usage error (exit 64) before the request is made.
  • The to examples now use a bare address. FreeAgent refuses the Name <address> form the docs' example (and the spec's) used.

EmailPayload is shared with estimates and credit notes, which take the same attachments array per the docs. Their spec gains the field, but only invoice send-email has the option. No endpoint is added, so make coverage is unchanged.

What The Sandbox Showed

Probed under the pinned X-Api-Version: 2026-09-01 only. Every successful call sends real mail, so this was a one-off probe with no integration test. One email was sent, to probe@example.com with email_to_sender: false; every other call was refused before sending. All four probe invoices were deleted.

  • attachments is read. A PDF plus a PNG in one send was a 200 with an empty body, and the invoice moved from Draft to Open.
  • Attachments are checked before the addresses. A send with no from therefore probes attachments without sending mail.
  • data, file_name and content_type must all be present. Leaving one out is a 422 that names an undocumented alternative, file_id on its own, which wasn't tried.
  • content_type is ignored, as on expenses:
    • FreeAgent detects the type from the bytes. Random bytes sent as image/png are refused, and PNG bytes sent as application/pdf pass.
    • .txt and .csv are accepted. The CLI keeps its pdf, png, jpg, jpeg and gif check.
  • Refusals:
    • Over 5MB is a 422.
    • A single object instead of an array is a 500.
    • A missing from is a 422 from must be a verified email address.
    • A named to is a 422 to is not a valid email address.

Code

  • Spec: a new EmailAttachmentPayload with data, file_name and content_type, all three required. EmailPayload gains attachments, an array of it.
  • InvoiceSendEmailCommand reads each --attachment through AttachmentFile and sends no attachments key when none is given, so existing sends are unchanged.

Tests

  • CLI unit test: an unsupported attachment throws AttachmentFileError and sendInvoiceEmail is never called.
  • 155 CLI, 90 API and 21 tools unit tests pass, and format and lint are clean.
  • No integration test, since each run would send an email.
  • The debug CLI was checked against the sandbox:
    • A dry run with a PDF and a PNG shows the documented body.
    • A .txt file and a missing path each exit 64.

🤖 Generated with Claude Code

EmailPayload gains an attachments array of a new EmailAttachmentPayload,
and invoice send-email takes a repeatable --attachment. The to examples
use a bare address, since FreeAgent refuses a named recipient.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dinoconstantinou87
dinoconstantinou87 merged commit 9f5fc2c into main Oct 15, 2026
4 checks passed
@dinoconstantinou87
dinoconstantinou87 deleted the feat/invoice-email-attachments branch October 15, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant