Use case

Document-processing agents: invoices, receipts and tax forms by email

Suppliers email invoices, employees forward receipts, contractors send W-9s. Your agent gets the text of every attachment and, when it asks, the fields as JSON that matches a schema.

Accounts payable, expense reports and vendor onboarding all start the same way: someone emails a document. An agent can do the routine part of that work if it can read the attachment reliably, knows when the text is ready, and gets the numbers in a shape it can check. Agentboxd reads every inbound attachment on its own servers and hands the agent the result.

#The flow

  • Address. Give the agent an inbox such as invoices@mail.yourcompany.com on a custom domain, and tell suppliers to send there.
  • Reading. When mail arrives, each attachment is read in the background: PDFs, Word and Excel files and CSVs directly, scans and phone photos by OCR (English, French and Arabic). Nothing leaves our servers for this. See Document extraction.
  • Ready. attachment.extracted fires per attachment. The message’s attachments[].extraction shows the status, method (text or OCR), pages and language; GET /v1/messages/:id/attachments/:attachmentId/text returns the text.
  • Fields. POST /v1/messages/:id/attachments/:attachmentId/extract with invoice, receipt, tax_form or your own JSON Schema returns JSON that has been validated against the schema. Missing values come back null, not guessed.
  • Checks. The agent matches the result against your own records (a purchase order, an employee, an expected amount) and books it, or labels the message for a person.
  • Search. Attachment text is part of full-text search, so “the invoice that mentioned PO-7731” is one GET /v1/search call away.

#The handler

A webhook handler that extracts an invoice as soon as its text is ready, books small invoices that match a purchase order, and leaves the rest for review:

invoices.ts
import { Agentboxd } from 'agentboxd';

const mr = new Agentboxd();

/** Webhook handler for attachment.extracted: one event per attachment whose text is ready. */
export async function onExtracted(event: { data: { message_id: string; attachment: { id: string; filename: string | null } } }) {
  const { message_id, attachment } = event.data;
  const invoice = await mr.messages.extractAttachment(message_id, attachment.id, { schema: 'invoice' });
  const d = invoice.data as { invoice_number: string | null; total: number | null; currency: string | null; vendor: { name: string | null } | null };

  // Values come from an emailed document: check them against your own records before acting.
  const po = d.invoice_number ? await findPurchaseOrder(d.vendor?.name, d.total) : null;
  if (po && d.total !== null && d.total <= 500) {
    await bookInvoice(po, d); // your ERP / accounting API
    await mr.messages.update(message_id, { add_labels: ['invoice:booked'] });
  } else {
    await mr.messages.update(message_id, { add_labels: ['invoice:review'] }); // a person decides
  }
}

declare function findPurchaseOrder(vendor: string | null | undefined, total: number | null): Promise<string | null>;
declare function bookInvoice(po: string, data: unknown): Promise<void>;

#What makes documents hard here, and what handles it

ProblemWhat Agentboxd gives the agent
Scanned PDFs and phone photosOCR on our servers when a PDF has no text layer or the attachment is an image; method: "ocr" says so.
“Invoice.pdf” that is really something elseFormats detected from the file’s bytes, not its name; damaged, oversized and unsafe files fail with a clear error.code.
Model output that doesn’t fit your systemJSON validated against your schema, with one automatic repair; still invalid answers 422 instead of bad data.
A PDF that says “pay this account instead”Extracted text and structured results are marked untrusted; categorisation scores injection and phishing on the text too.
Knowing when it is doneattachment.extracted / attachment.extraction_failed events per attachment, on webhooks and the realtime stream.

#Plans and privacy

Reading attachments counts pages: 100 a month on Free, 5,000 on Builder and 50,000 on Team. Structured extraction is 500 a month on Builder and 5,000 on Team (not on Free), with overage on paid plans. See Plans and limits.

#Build it

  • In Claude or Cursor: the MCP server has get_attachment_text and extract_attachment built in.
  • No code: an n8n workflow can call the extract endpoint from the webhook.
  • Every field, error code and limit: Document extraction.