Recipe: expense capture
Turn the receipts and invoices in a user's mail into expense records they review and approve in your application.
Why it matters: nobody forwards receipts or types amounts. Expenses arrive with their source email and file attached, the uncertain values are flagged for a quick check, and approved records are ready for the books.
Scopes: operations:expenses:write (includes operations:expenses:read), and profile:read to
open the original email.
Webhooks: expenses.first_completed, expenses.completed.
1. Find the expenses to review
Expenses with every required field filled wait in pending_approval. Search for them, newest first:
curl https://api.bind.co/v1/operations/expenses/search \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"status": ["pending_approval"], "sort_by": ["-expense_date"], "page_number": 1, "page_size": 50}'
{
"items": [
{
"operation_id": "op_5b1f0c7e-8a2d-4c3b-9e61-2f7d4a9c0b13",
"operation_type": "expenses",
"status": "pending_approval",
"vendor_normalised": {"value": "Delta Air Lines", "bind_inference_confidence": 0.97},
"expense_date": {"value": "2026-10-14", "bind_inference_confidence": 0.99},
"total_amount": {"value": "412.60", "bind_inference_confidence": 0.98},
"currency_code": {"value": "USD", "bind_inference_confidence": 0.99},
"evidence": [
{"evidence_type": "email", "role": "primary", "created_at": "2026-10-14T16:04:30Z", "file": null,
"email": {"email_id": "eml_16nJ9iKYhhC", "email_sender": "receipts@delta.com", "email_timestamp": "2026-10-14T16:01:47Z"}}
]
}
],
"total_items": 1, "total_pages": 1, "page_number": 1, "page_size": 50
}
Search "status": ["active"] for expenses not yet ready for review; each one's
assessment.completeness_state says whether a required field is still missing (incomplete).
2. Show what needs attention
Use each field's bind_inference_confidence to decide where the user should look. A simple rule:
show values at 0.9 or above as they are, and highlight the rest for review. Tune the threshold on
your users' data; the score is bind.co's relative confidence, not a probability (see
Reading confidence).
Show the evidence next to the record: the email's sender and date, and for a file its name and
file_summary. GET /v1/emails/{email_id}/web-link gives a link that opens the original email in the
user's mailbox; it needs profile:read.
3. Let the user decide
| The user | Call | Result |
|---|---|---|
| Approves it as it is | POST /v1/operations/expenses/{operation_id}/confirm |
approved |
| Fixes a value | PATCH /v1/operations/expenses/{operation_id} with only the fields changed |
corrected |
| Says it is not an expense | POST /v1/operations/expenses/{operation_id}/reject |
rejected |
A correction:
curl -X PATCH https://api.bind.co/v1/operations/expenses/op_5b1f0c7e-8a2d-4c3b-9e61-2f7d4a9c0b13 \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"total_amount": "412.00"}'
Once a person has decided, later mail never changes the values: new evidence is still listed under
the record. To make sure you change the version the user saw, read the record with
GET /v1/operations/expenses/{operation_id} (search answers carry no ETag) and send its ETag in
If-Match; see Pagination and idempotency.
4. Stay current
On expenses.completed, search again, for example sorted by newest:
{"type": "expenses.completed", "data": {"user_inbox_fetch_id": "fch_4hT0q2Lw9Za", "operation_type": "expenses", "operations_created": 3, "operations_updated": 5}}
Duplicates bind.co finds later are merged into one record; a stored operation_id that starts
answering 404 was merged. The Operations guide has the full lifecycle.
Make it yours
- Spend management: employees stop uploading receipts; their expenses appear for approval.
- Bookkeeping firms: close the month with every receipt from the client's mail already in.
- Personal finance or freelancer tools: a running list of business costs, ready at tax time.
- Other records: the same steps work for every type in
GET /v1/operations; only the type name and its fields change.