Skip to main content

Overview

Tesouro flags payables that look like duplicates of each other. It never cancels a payable on its own: an organization user reviews each flagged pair and decides whether to keep both or cancel the copy. Two payables of the same organization are considered possible duplicates when they have the same invoice number (document_id) and the same vendor. Tesouro compares vendors in one of two ways:
  • If both payables are linked to a counterpart, they must have the same counterpart_id.
  • If at least one of them isn’t linked to a counterpart yet, Tesouro compares the vendor name that OCR read from the invoice (counterpart_raw_data.name). The comparison ignores case, punctuation, accents, and trailing legal suffixes such as Inc, LLC, or Ltd, so “Acme Inc.” and “ACME” match.
A payable without an invoice number never matches anything, and an empty invoice number is treated the same as a missing one. A duplicate match always links exactly two payables and is visible from both of them. Either payable can be used to review it, and the result is the same.

When detection runs

Detection runs automatically, and its result is already included in the response of the request that triggered it. There is no delay and no separate job to wait for. Tesouro checks for duplicates when:
  • a payable is created from data;
  • OCR finishes processing an uploaded or emailed invoice;
  • a payable is updated and its counterpart_id or document_id changes;
  • a counterpart is linked automatically while the payable moves through its lifecycle, for example on submission for approval.
An update can also remove a warning. If a user corrects the invoice number of a payable and it no longer collides with anything, the pending match disappears.

The duplicates array

Every payable returned by the API, whether in a list, on its own, or in the response to an update or a status change, includes a duplicates array. It is empty when there is nothing to report:
A few things to keep in mind when you build on top of the array:
  • It contains dismissed and confirmed matches too, so it doubles as the review history.
  • It leaves out matches whose other payable is canceled. A copy that was already cancelled no longer needs a warning. Tesouro still records these matches: to find payables that match a cancelled payable, such as an invoice resent after fraud, use the duplicate_matched_payable_status filter. Their duplicates array stays empty for that match.
  • Suspected matches come first, then confirmed, then dismissed. Within each group the newest match comes first. So if the first entry is suspected, the payable needs attention.
  • It returns at most 100 entries. When a payable has more, the dismissed and confirmed history is cut first, never the suspected matches.
  • A match also disappears if the other payable is deleted.
  • To tell whether a match is resolved, check status or resolved_at. Don’t rely on resolved_by_user_id, which can be null for a resolved match.
To show details of the other payable, such as its invoice number or amount, call GET /payables/{payable_id} with the matched_payable_id.

Review a duplicate

A user resolves a match in one of two ways. Both decisions are final: once a match is dismissed or confirmed, it can’t be changed.

Keep both payables

If the two payables are different invoices that happen to share a number, dismiss the match:
payable_id can be either payable of the pair, and duplicate_id is the id from its duplicates array. The response is the updated match:
A dismissed pair is never flagged again, even if one of the payables is edited later and its invoice number collides again.

Cancel the duplicate

If the payable really is a duplicate, cancel it first and then confirm the match. These are two separate requests, because confirming a match does not change the status of any payable.
  1. Cancel the duplicate payable with POST /payables/{payable_id}/cancel.
  2. Confirm the match:
The response has the same shape as for a dismissal, with "status": "confirmed". If the cancellation succeeds but the confirmation fails, the payable stays cancelled and the match stays suspected. You can retry the confirmation at any time. Once the copy is cancelled, the match no longer appears in the duplicates array of the original payable, because its other payable is now canceled.
A match can be resolved only once. Dismissing or confirming a match that is already resolved returns 409 Conflict. After a successful request, disable both actions in your UI.

Find payables with duplicates

GET /payables accepts filters that select payables by their duplicate matches. These filters choose which payables are returned. They don’t change the duplicates array of the returned payables. Every filter asks whether at least one matching entry exists. A payable with one dismissed match and one suspected match is returned by duplicate_match_status__not_in=dismissed, because the suspected match qualifies. When you combine several of these filters, all conditions must hold for the same match. A payable is not returned just because one of its matches meets one condition and another match meets the other. The duplicate_match_status filters follow the same rule as the duplicates array and skip cancelled payables:
  • Matches whose other payable is canceled don’t count, unless you also pass a duplicate_matched_payable_status filter, which then decides on its own.
  • Payables that are themselves canceled are not returned.
Some examples: You don’t need these filters to show a warning next to each row of a payables list. Every row already carries its own duplicates array, so you can check it directly on the page you have.

Permissions

To view and resolve duplicate matches, organization users need these permissions: The matches come with the payable, so anyone who can view a payable also sees its duplicates array.