Validating Receipts
Submit receipts by file or URL against a campaign's rules and interpret the pass/fail response, validation keys, and matched line items.
Once your campaign settings exist, submit each receipt against the campaign by file upload or URL. Validation is synchronous — the pass/fail result and the validation response fields documented below return in the same HTTP response, so there are no webhooks to build. Typical timings are covered in Performance & Response Times.
Endpoints
| Input type | Endpoint |
|---|---|
| File upload | POST https://api.taggun.io/api/validation/v1/campaign/receipt-validation/file |
| URL | POST https://api.taggun.io/api/validation/v1/campaign/receipt-validation/url |
API console: Validate Receipts
Availability
Availability: Receipt validation is included on the Advanced plan and above. Enable Campaign Validation in your account Feature Settings when it is available on your plan. See Plans & Feature Access and Setting Up Validation Campaigns.
File Upload Request
Send as multipart/form-data.
| Field | Type | Description | Required |
|---|---|---|---|
file | file | Use a supported receipt or invoice file that meets the current Document Types & File Requirements. | Yes |
campaignId | string | The campaign ID to validate the receipt against. Maximum 50 characters. | Yes |
referenceId | string | Optional receipt identifier, maximum 50 characters. See Duplicate & Similarity Detection. | No |
userId | string | Optional end-user identifier associated with the submission and returned with matched receipts, when available, so cross-user reuse can be investigated. Maximum 50 characters. | No |
subAccountId | string | Identifier used to attribute usage within your Taggun account for reporting. Maximum 100 characters. | No |
ipAddress | string | IPv4 or IPv6 address of the end user. | No |
near | string | Geographic hint for merchant search, typically city, state, country. | No |
language | string | Two-letter language hint. Leave empty for auto-detection. | No |
curl --request POST \
--url https://api.taggun.io/api/validation/v1/campaign/receipt-validation/file \
--header 'accept: application/json' \
--header 'apikey: YOUR_API_KEY' \
--header 'content-type: multipart/form-data' \
--form file='@receipt.jpg' \
--form campaignId=coffee-june-2026 \
--form userId=customer-456URL Request
Send as JSON. The URL must use HTTPS and be reachable by Taggun. HTTP URLs and localhost, loopback, link-local, or private-network hosts are rejected. Use headers when the HTTPS resource requires authorised access.
| Field | Type | Description | Required |
|---|---|---|---|
url | string | HTTPS URL containing the receipt file and reachable by Taggun. Private/local network targets are rejected. | Yes |
headers | object | Optional headers to forward when the HTTPS resource requires authorised access. | No |
campaignId | string | The campaign ID to validate the receipt against. Maximum 50 characters. | Yes |
referenceId / userId / subAccountId / ipAddress / near / language | — | Same as the file upload request above. | No |
{
"url": "https://example.com/receipts/receipt-123.png",
"campaignId": "coffee-june-2026",
"userId": "customer-456"
}Response
The response contains the campaign validation result and the selected extracted fields in the table below.
| Field | Type | Description |
|---|---|---|
successful | boolean | true when all enabled validation checks passed; false when one or more failed. |
failedValidations | string[] | Validation keys that failed. |
passedValidations | string[] | Validation keys that passed. |
productLineItems | optional object[] | null | Root property containing extracted validation line items when returned. Item properties are optional. |
matchedProductLineItems | optional object[] | null | Root property containing items matched by configured campaign rules when returned. Item properties are optional. |
date | string | null | Receipt transaction date, when detected. |
merchantName | string | null | Merchant name, when detected. |
merchantAddress / merchantCity / merchantPostalCode / merchantState / merchantCountryCode | object | null | Merchant-location result objects, when detected. The public schema does not guarantee a fixed inner shape; treat their properties as optional. |
productCodes | string[] | null | Product codes detected or validated for the campaign. |
balanceAmount | number | null | Balance owing detected on the receipt. |
smartValidate | object | Boolean results for configured SmartValidate prompts. |
totalAmount | number | null | Receipt total amount, when detected. |
currencyCode | string | null | Currency code for the total amount, when detected. |
receiptNumber / invoiceNumber | string | null | Receipt or invoice number, when detected. |
similarReceipts | object[] | null | Root-level similar receipt matches when similarity checking is enabled. |
tamperDetection | object | null | Root-level tamper result when enabled; read tamperDetection.isTampered and tamperDetection.tamperedScore. |
digitalDetection | object | null | Root-level digital result when enabled; values remain nested at digitalDetection.data.isDigital and digitalDetection.data.digitalScore. |
trackingId | string | Taggun tracking ID for this validation request. See Contacting Support for the reporting workflow. |
targetRotation | number | null | Numeric image-rotation value returned by processing. Units, direction, and normalisation are not currently documented. |
Properties whose value is undefined may be absent. Where the schema permits null, handle it unless endpoint-specific semantics are documented.
The inner fraud-result shapes differ between validation and verbose extraction. Compare the exact paths in Data Fields That Taggun Extracts.
Product line item object
| Field | Type | Description |
|---|---|---|
name | string | null | Product or service name. |
unitPrice | number | null | Unit price, when detected. |
totalPrice | number | null | Total line price, when detected. |
quantity | number | null | Quantity, when detected. |
sku | string | null | Stock keeping unit, when detected. |
Validation Keys
These keys appear in passedValidations or failedValidations depending on the campaign settings and the receipt result. They are the machine-readable contract your integration should branch on.
| Key | Meaning |
|---|---|
date_validated | Receipt date was checked against the campaign date window. |
merchant_name_validated | Merchant name was checked against the campaign merchant rules. |
product_code_validated | Product codes were checked against the campaign product code rules. |
product_line_items_validated | Product line items were checked against the campaign line item rules. |
balance_amount_validated | Balance owing was checked against the campaign maximum. |
fraud_detection_unique_receipt_validated | Receipt similarity or duplicate checking was evaluated. |
fraud_detection_genuine_receipt_validated | Tamper detection was evaluated. |
fraud_detection_complete_receipt_validated | Receipt completeness was evaluated (non-receipt or incomplete receipt detection). |
| SmartValidate output key | A non-skipped prompt's output key appears as a validation key, for example is_credit_card. |
When campaign settings use balanceOwing.skip: true, balanceAmount can still be returned, but balance_amount_validated is not added to passedValidations or failedValidations and does not affect successful. The balanceOwing schema still requires max; see Campaign Settings Reference.
Worked Example
A receipt fails the product-code check, passes the date and merchant checks, and returns the approved generic credit-card-payment SmartValidate illustration:
{
"successful": false,
"failedValidations": ["product_code_validated"],
"passedValidations": ["date_validated", "merchant_name_validated", "is_credit_card"],
"productLineItems": [
{
"name": "Coffee Beans",
"unitPrice": 12.5,
"totalPrice": 25,
"quantity": 2
}
],
"matchedProductLineItems": [],
"date": "2026-06-12T00:00:00.000Z",
"merchantName": "Fresh Market",
"productCodes": [],
"balanceAmount": 0,
"smartValidate": { "is_credit_card": true },
"totalAmount": 25,
"currencyCode": "NZD",
"receiptNumber": "R12345",
"invoiceNumber": null,
"similarReceipts": [],
"tamperDetection": null,
"digitalDetection": null,
"trackingId": "7f64b9e0-0000-0000-0000-000000000000"
}Reading it: successful is false because product_code_validated failed — no qualifying product codes were found — even though the date, merchant, and generic is_credit_card checks passed. The SmartValidate example shows the response shape and does not describe a particular customer's production use case. Your workflow may use actionable, non-fraud failures to request a correction or resubmission. Keep fraud-related validation keys server-side; use neutral customer messaging and route suspicious submissions to your review or rejection workflow.
When similarity checking is enabled and a near-duplicate exists, similarReceipts contains the matched receipts with their trackingId, your original referenceId/userId where available, and a similarityScore. similarityScore is a numeric similarity signal; 1.0 is the maximum returned score, not proof of identity or authenticity. See Duplicate & Similarity Detection for score interpretation.
Status codes
| Code | Meaning |
|---|---|
200 | Validation completed (check successful for the outcome). |
400 | Bad request — invalid parameters or file, or an unknown campaignId. |
401 | The apikey header is missing. |
403 | Authentication credentials are invalid. Campaign Settings feature availability uses the separately documented operation-specific 400 family. |
For general error handling, see Error Handling & Status Codes.
For an unknown campaignId, both file and URL requests return 400 with { "statusCode": 400, "error": "Bad Request", "message": "The Campaign ID is not found" }. A deleted campaign is expected to fail, but its exact Receipt Validation status and body remain unconfirmed and must not be inferred from the unknown-campaign branch.
Managing Settings
To retrieve, update, list, or delete campaign settings, see Managing Campaigns.
Need something not covered here? Contact [email protected] — see Contacting Support.
Updated about 21 hours ago