Receipt to charge
Which one card charge paid for this receipt? Decision id receipt_match, a choice. Answers: a transaction id, or "none".
Built and tested, not yet deployed. Early access.
On this page
Example
curl "$UNDERSTANDING_API_URL/v1/understand" \ -H "Authorization: Bearer $UNDERSTANDING_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "decision": "receipt_match", "input": { "receipt": { "merchant": "Everlane", "total": 128, "date": "2026-10-05T18:00:00Z" }, "transactions": [ { "id": "t1", "merchant": "EVERLANE", "amount": 128, "date": "2026-10-05T18:02:00Z" }, { "id": "t2", "merchant": "UBER *EATS", "amount": 32, "date": "2026-10-05T21:30:00Z" }, { "id": "t3", "merchant": "EVERLANE", "amount": -40, "date": "2026-10-09T10:00:00Z" } ] } }'
const base = process.env.UNDERSTANDING_API_URL; const key = process.env.UNDERSTANDING_API_KEY; const body = { decision: "receipt_match", input: { receipt: { merchant: "Everlane", total: 128, date: "2026-10-05T18:00:00Z", }, transactions: [ { id: "t1", merchant: "EVERLANE", amount: 128, date: "2026-10-05T18:02:00Z", }, { id: "t2", merchant: "UBER *EATS", amount: 32, date: "2026-10-05T21:30:00Z", }, { id: "t3", merchant: "EVERLANE", amount: -40, date: "2026-10-09T10:00:00Z", }, ], }, }; const res = await fetch(`${base}/v1/understand`, { method: "POST", headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json", }, body: JSON.stringify(body), }); const result = await res.json();
import os import requests base = os.environ["UNDERSTANDING_API_URL"] key = os.environ["UNDERSTANDING_API_KEY"] body = { "decision": "receipt_match", "input": { "receipt": { "merchant": "Everlane", "total": 128, "date": "2026-10-05T18:00:00Z", }, "transactions": [ { "id": "t1", "merchant": "EVERLANE", "amount": 128, "date": "2026-10-05T18:02:00Z", }, { "id": "t2", "merchant": "UBER *EATS", "amount": 32, "date": "2026-10-05T21:30:00Z", }, { "id": "t3", "merchant": "EVERLANE", "amount": -40, "date": "2026-10-09T10:00:00Z", }, ], }, } res = requests.post( f"{base}/v1/understand", headers={"Authorization": f"Bearer {key}"}, json=body, timeout=10, ) result = res.json()
{ "decision": "receipt_match", "type": "choice", "answer": "t1", "probability": 0.95, "reasons": [ "amount_exact", "name_match", "within_a_day", "rule_agrees" ], "abstain": false }
Input
receiptobjectrequiredThe receipt to match.
Show 3 fields
merchantstringrequiredThe merchant on the receipt, 1 to 200 characters.
totalnumber or stringrequiredThe total in US dollars.
datestringrequiredWhen it was bought, ISO 8601.
Also accepts
purchasedAt.
transactionsarray of transactionsrequiredThe card activity to search, up to 500.
Show 6 fields
idstringrequired1 to 100 characters, unique within the list. Can’t be
"none", which means “no match”.merchantstringoptionalThe statement descriptor, 1 to 200 characters.
Also accepts
merchantName.mccstringoptionalThe four-digit merchant category code, as a string.
Also accepts
merchantCategoryCode.amountnumber or stringrequiredUS dollars: positive for a charge, negative for money back. A string such as
"$1,234.56"works too.Also accepts
usdAmount.datestringrequiredAn ISO 8601 date with a zone, for example
2026-10-05T18:00:00Z.Also accepts
createdAt.statusstringoptionalDefaults to
"approved". A"declined"charge is never a receipt’s candidate.
What our engine adds
- Only charges can be candidates: positive amounts that aren’t declined. Refunds are never offered.
- The 30 candidates nearest in time to the receipt go to the model; the rest are left out and counted.
- For each candidate: the amount difference in dollars and percent, the hours after the receipt and the name similarity.
- Our engine’s own pick, so the reasons can say whether the model agrees.
What the model is asked
- Which one transaction paid for this receipt, or
noneif no single transaction did. Not to guess.
Reason codes
| Code | When |
|---|---|
amount_exact | The chosen charge equals the receipt total (within half a cent). |
amount_within_3pct | Within 3% of the total. |
amount_differs | More than 3% away. |
name_match | Name similarity of 0.7 or more. |
name_differs | Name similarity under 0.7. |
within_a_day | Charged within 24 hours of the receipt. |
within_3_days | Within 72 hours. |
over_3_days | More than 72 hours apart. |
no_single_charge | The answer is "none". |
rule_agrees | The model’s pick equals our engine’s own pick (or both say none). |
rule_differs | It doesn’t. Review these cases first. |
rule_no_purchases | Settled without the model: no positive, non-declined transaction. |
model_declined | The model declined the question. |
When a rule settles it
If no transaction is a positive, non-declined charge, the answer is "none" with rule_no_purchases, probability 1, with no model call.
Errors
Each is a 400 with field naming the part at fault.
input.receipt must be an objectinput.receipt.merchant must be text of 1 to 200 charactersinput.receipt.total must be an amount in US dollarsinput.receipt.date must be an ISO 8601 date<field> must be a transaction object<field>.id must be text of 1 to 100 characters<field>.id can't be "none"<field>.merchant must be text of 1 to 200 characters<field>.mcc must be a four-digit merchant category code<field>.amount must be an amount in US dollars<field>.date must be an ISO 8601 dateinput.transactions must be a list of up to 500 transactionsinput.transactions ids must be unique
Evaluation
The Understanding API hasn’t been run on our eval yet. Evaluation
# Receipt to charge
Which one card charge paid for this receipt? Decision id `receipt_match`, a choice. Answers: a transaction `id`, or `"none"`.
Built and tested, not yet deployed. Early access.
## Example
The request in cURL:
```bash
curl "$UNDERSTANDING_API_URL/v1/understand" \
-H "Authorization: Bearer $UNDERSTANDING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"decision": "receipt_match",
"input": {
"receipt": {
"merchant": "Everlane",
"total": 128,
"date": "2026-10-05T18:00:00Z"
},
"transactions": [
{
"id": "t1",
"merchant": "EVERLANE",
"amount": 128,
"date": "2026-10-05T18:02:00Z"
},
{
"id": "t2",
"merchant": "UBER *EATS",
"amount": 32,
"date": "2026-10-05T21:30:00Z"
},
{
"id": "t3",
"merchant": "EVERLANE",
"amount": -40,
"date": "2026-10-09T10:00:00Z"
}
]
}
}'
```
The request in TypeScript:
```typescript
const base = process.env.UNDERSTANDING_API_URL;
const key = process.env.UNDERSTANDING_API_KEY;
const body = {
decision: "receipt_match",
input: {
receipt: {
merchant: "Everlane",
total: 128,
date: "2026-10-05T18:00:00Z",
},
transactions: [
{
id: "t1",
merchant: "EVERLANE",
amount: 128,
date: "2026-10-05T18:02:00Z",
},
{
id: "t2",
merchant: "UBER *EATS",
amount: 32,
date: "2026-10-05T21:30:00Z",
},
{
id: "t3",
merchant: "EVERLANE",
amount: -40,
date: "2026-10-09T10:00:00Z",
},
],
},
};
const res = await fetch(`${base}/v1/understand`, {
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
const result = await res.json();
```
The request in Python:
```python
import os
import requests
base = os.environ["UNDERSTANDING_API_URL"]
key = os.environ["UNDERSTANDING_API_KEY"]
body = {
"decision": "receipt_match",
"input": {
"receipt": {
"merchant": "Everlane",
"total": 128,
"date": "2026-10-05T18:00:00Z",
},
"transactions": [
{
"id": "t1",
"merchant": "EVERLANE",
"amount": 128,
"date": "2026-10-05T18:02:00Z",
},
{
"id": "t2",
"merchant": "UBER *EATS",
"amount": 32,
"date": "2026-10-05T21:30:00Z",
},
{
"id": "t3",
"merchant": "EVERLANE",
"amount": -40,
"date": "2026-10-09T10:00:00Z",
},
],
},
}
res = requests.post(
f"{base}/v1/understand",
headers={"Authorization": f"Bearer {key}"},
json=body,
timeout=10,
)
result = res.json()
```
Response: 200 OK (the probability is a stand-in)
```json
{
"decision": "receipt_match",
"type": "choice",
"answer": "t1",
"probability": 0.95,
"reasons": [
"amount_exact",
"name_match",
"within_a_day",
"rule_agrees"
],
"abstain": false
}
```
## Input
- `receipt` (object, required): The receipt to match. Also accepts `purchasedAt`.
- `merchant` (string, required): The merchant on the receipt, 1 to 200 characters.
- `total` (number or string, required): The total in US dollars.
- `date` (string, required): When it was bought, ISO 8601. Also accepts `purchasedAt`.
- `transactions` (array of transactions, required): The card activity to search, up to 500. Also accepts `merchantName`.
- `id` (string, required): 1 to 100 characters, unique within the list. Can’t be `"none"`, which means “no match”.
- `merchant` (string, optional): The statement descriptor, 1 to 200 characters. Also accepts `merchantName`.
- `mcc` (string, optional): The four-digit merchant category code, as a string. Also accepts `merchantCategoryCode`.
- `amount` (number or string, required): US dollars: positive for a charge, negative for money back. A string such as `"$1,234.56"` works too. Also accepts `usdAmount`.
- `date` (string, required): An ISO 8601 date with a zone, for example `2026-10-05T18:00:00Z`. Also accepts `createdAt`.
- `status` (string, optional): Defaults to `"approved"`. A `"declined"` charge is never a receipt’s candidate.
## What our engine adds
- Only charges can be candidates: positive amounts that aren’t declined. Refunds are never offered.
- The 30 candidates nearest in time to the receipt go to the model; the rest are left out and counted.
- For each candidate: the amount difference in dollars and percent, the hours after the receipt and the name similarity.
- Our engine’s own pick, so the reasons can say whether the model agrees.
## What the model is asked
- Which one transaction paid for this receipt, or `none` if no single transaction did. Not to guess.
## Reason codes
| Code | When |
| --- | --- |
| `amount_exact` | The chosen charge equals the receipt total (within half a cent). |
| `amount_within_3pct` | Within 3% of the total. |
| `amount_differs` | More than 3% away. |
| `name_match` | Name similarity of 0.7 or more. |
| `name_differs` | Name similarity under 0.7. |
| `within_a_day` | Charged within 24 hours of the receipt. |
| `within_3_days` | Within 72 hours. |
| `over_3_days` | More than 72 hours apart. |
| `no_single_charge` | The answer is `"none"`. |
| `rule_agrees` | The model’s pick equals our engine’s own pick (or both say none). |
| `rule_differs` | It doesn’t. Review these cases first. |
| `rule_no_purchases` | Settled without the model: no positive, non-declined transaction. |
| `model_declined` | The model declined the question. |
## When a rule settles it
If no transaction is a positive, non-declined charge, the answer is `"none"` with `rule_no_purchases`, probability 1, with no model call.
## Errors
Each is a 400 with `field` naming the part at fault.
- `input.receipt must be an object`
- `input.receipt.merchant must be text of 1 to 200 characters`
- `input.receipt.total must be an amount in US dollars`
- `input.receipt.date must be an ISO 8601 date`
- `<field> must be a transaction object`
- `<field>.id must be text of 1 to 100 characters`
- `<field>.id can't be "none"`
- `<field>.merchant must be text of 1 to 200 characters`
- `<field>.mcc must be a four-digit merchant category code`
- `<field>.amount must be an amount in US dollars`
- `<field>.date must be an ISO 8601 date`
- `input.transactions must be a list of up to 500 transactions`
- `input.transactions ids must be unique`
## Evaluation
The Understanding API hasn’t been run on our eval yet. [Evaluation](/api/docs/evaluation)
Source: /api/docs/decisions/receipt-match