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.comon 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.extractedfires per attachment. The message’sattachments[].extractionshows the status, method (text or OCR), pages and language;GET /v1/messages/:id/attachments/:attachmentId/textreturns the text. - Fields.
POST /v1/messages/:id/attachments/:attachmentId/extractwithinvoice,receipt,tax_formor your own JSON Schema returns JSON that has been validated against the schema. Missing values come backnull, 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/searchcall 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:
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
| Problem | What Agentboxd gives the agent |
|---|---|
| Scanned PDFs and phone photos | OCR 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 else | Formats 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 system | JSON 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 done | attachment.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_textandextract_attachmentbuilt in. - No code: an n8n workflow can call the extract endpoint from the webhook.
- Every field, error code and limit: Document extraction.