ACME, Inc.. QuickBooks has Acme Inc, or Acme LLC, or both. Before you
add the customer, or put an invoice on it, you need to know which record your app means.
QuickBooks can’t tell you. A full_name lookup is exact: it ignores case and the spaces at either end, and
nothing else. The only looser filter is “contains” (or “starts with”, “ends with”), on one list at a time,
unranked, so ACME, Inc. doesn’t find Acme Inc, and Smith, John doesn’t find John Smith.
Matching does. QuBe Sync keeps an index of the names in the company file, and a match ranks them against
yours in milliseconds. It ignores case, accents, punctuation, company suffixes such as Inc and LLC, spacing
and word order, finds near misses, keeps store and unit numbers apart, and can compare an account number,
email, phone, city and postal code too.
Matching needs a
realtime connection (or a simulated one in your sandbox). A standard connection
is refused with matching_requires_realtime. Your sandbox app isn’t billed, so switching a sandbox
connection to realtime costs nothing: do it from its page in the dashboard. A
workflow’s match state is never refused: on a standard connection it falls back to
QuickBooks’ own filters.Match a name
Send the name in the JSON body, never the query string: The answer comes back at once, a200. This file has both Acme LLC and Acme Corp. Both are the same
name once punctuation and suffixes are set aside, so neither is certain, and the one with the email you specified comes first:
Response
string
required
Which lists to look in:
customer, vendor, employee, other_name, or entity for all four (they
share one name space in QuickBooks); item for every item type; or account, class, standard_terms,
date_driven_terms, sales_tax_code, payment_method, ship_method, price_level, customer_type,
vendor_type, job_type, sales_rep or customer_msg.string
required
The name your app has for the record, at most 255 characters. With a
parent, its own name only:
Kitchen, not Smith:Kitchen. Its numbers must agree with a record’s: see
numbered locations.string
For a job, a sub-item, a subaccount and the like: the parent’s full name or its
list_id, so only that
parent’s children are matched. Without one, children match on their own name too, and say whose they
are (a job under Smith). Taken for customer, entity, item, account, class, customer_type,
vendor_type and job_type.object
For
customer, vendor, employee, other_name and entity: what else your app knows, as
account_number, email, phone, postal_code, city and company_name. The same account number
makes a strong match whatever the name. The same email, phone or postal code, with a name at least 0.4
alike (see score), makes a strong match: Bill Johnson with Bill’s email finds William Johnson. Each
one in common is a reason, and ranks a candidate higher; a city or company name does only that. See
distinguishing fields for how each is compared.boolean
default:"false"
true answers only exact or none: the one record with this full name, as QuickBooks compares names,
or nothing. Never a fuzzy match, so you can act on it without asking.number
This call’s candidate threshold, from 0.3 to 1.0, in place of the default 0.44. Raise it to list only close names;
lower it to list more. Not taken with
exact_only.What the answer means
match is the first of these that applies:
Each record in
record or candidates has:
string
QuickBooks’ id for the record: what a reference such as
customer_ref or item_ref names it by.string
Its full name. A job’s or a sub-item’s starts with its parent’s:
Smith:Kitchen.string
The list it’s in:
customer, vendor, an item type such as item_service, and so on.boolean
false for an inactive record. Inactive records are matched too: QuickBooks hides them, but they keep
their names, and adding another with the same name fails with 3100.number
How alike the names are, from 0 to 1, once case, accents, punctuation and suffixes are set aside:
1.0
for an exact match. A candidate has at least 0.44, unless it’s there for another reason, such as two
letters swapped (Desing for Design), which scores low on a short name.string[]
Why it matched, as phrases you can show:
same name, same after removing punctuation and suffixes,
same words in a different order, same apart from spacing, two letters swapped, similar name, or
different name for a record found by its account number alone; then same number; then
same account number, same email, same phone, same postal code, same city or
same company name; and a job under Smith for a child matched without a parent.string | null
Its email, lower-cased, for a person to tell candidates apart.
null when it has none, or isn’t a
customer, vendor, employee or other name.string | null
Its phone, for a person to tell candidates apart: a 10-digit one as
512-555-0142. null when it
has none.integer
For
candidates: how many records qualified, of which candidates lists the first 10. 0 for any
other tier.boolean
false when candidates_total is a lower bound: a match reads at most 50 records, the most alike first,
and it read that many, so more may qualify.Numbered locations and crowded files
Some company files are full of names that differ only by a number: franchise locations (Subway #1234,
Subway #1235, and so on), units, a customer’s jobs by year. On their letters alone those names look
nearly the same: Subway 1234 and Subway 1235 are 0.71 alike. So matching treats the numbers as what
tells records apart.
Numbers must agree. A word with a digit in it is a number to a match: 1234 in Subway #1234, 4B
in Unit 4B. Leading zeros don’t count, so #01234 is 1234. When your name and a record’s both have
numbers, the record must have every number you sent. One with a different number instead is never
offered, no matter the other similarities. When exactly one record has the same numbers and a name at least 0.4
alike, it’s a strong match, with the reason same number. A record with your numbers and more can be a
candidate, with the reason the number asked, and more, but never a strong match.
A name with a number, asked for without one, is matched on the rest of the name, so
Subway lists the
stores. Two things outrank a different number: the exact full name, and the account_number you sent.
When the list is crowded
Whencandidates_total is more than the 10 in candidates, or candidates_total_complete is false, the
name alone doesn’t pick out a record. Don’t show your user a pick list of near-identical names. Ask for what
tells them apart: the store number, the city, the postal code or your account number for it. Then match
again with that:
The one store with that postal code is a strong match. If several share it, the ones in Austin come first.
Distinguishing fields
Send what your app knows infields. Each one in common is a reason and ranks a candidate higher, and
some make a match strong on their own:
The
city and postal_code are compared with a customer’s billing address, and with a vendor’s, an
employee’s or an other name’s own address.
Tighter or looser, for one call
exact_only: trueanswersexactornone, never a fuzzy match. Use it when your app should act only on a certain match, as when it’s about to create the record if it isn’t there.min_scoremoves the candidate threshold from 0.44, anywhere from 0.3 to 1.0. Raise it to see only close names, or lower it to cast wider.exactandstrongdon’t change, and a record two letters swapped stays a candidate.
Match once, then keep the list_id
Matching is for the records your app has no QuickBooks id for yet. Once your user confirms a match, store itslist_id on your own record, and from then on refer to the QuickBooks record by it, such as
customer_ref: {list_id} on an invoice. A list_id survives renames; a name doesn’t. It’s unique only within that
list family (customers, vendors, employees and other names share one) of that company file, so keep it with the connection it came from (see
Making requests).
- Match again only when the
list_idstops working. That happens when the record is deleted, or merged into another, in QuickBooks: a lookup by thelist_idanswers status code 500 with no record, and a request that references it is refused with 3120 (see Error handling). If your app keeps in step with thepull_changesworkflow, a merge comes through as a deletion of the record merged away. - Give the records you create an id you can match on. An app that creates customers can set their
account_numberto its own id for them, and match on that later, even after someone renames the customer in QuickBooks. Check first that the company doesn’t already use account numbers for something else.
In a workflow
A workflow does the same match as one of its steps: amatch state. It takes the endpoint’s
body as templates (kind, name, parent, fields, exact_only and min_score), so one chart serves any
record, and it always raises success, with the endpoint’s answer in response.data. The chart branches on
response.data.match:
look_up
exact match is used at once, none ends the run, a crowded answer asks your app for a city or
postal code and matches again, and a strong match or a few candidates go to a wait state that asks your
app which one is meant. The same rule as everywhere: only use exact without asking.
The fallback. On a connection downgraded to standard mid-run, or if the index’s build gives up, the step sends QuickBooks’ own
filters instead, as ordinary requests of the run: the exact full name, and the “contains” name filter, for
each list the
kind covers. The answer has the same shape, with index.state unavailable. It is exact,
or candidates without scores (score is null, with the reason contains the name), or none, and
never strong. exact_only skips the “contains” query, and min_score and fields have nothing to apply
to, so they are ignored. So the run finishes, without the ranking, punctuation, suffixes, word order,
typos and other fields the index matches on.
Or let a template do it. The shipped resolve template finds a list record of any kind (a customer, a
job under its parent, a vendor, an item, an account) by the list_id you stored, then by name. It asks your
app about near matches, or for a detail when too many are alike, and reads the record whole before it’s
used. ensure creates what resolve doesn’t find, asking when the name is held elsewhere in its name
space. add_transaction makes sure of every record a transaction names before adding it. See
Pick a template.
The state’s keys, its events and what the run records are under
Match states, and
Matching names in a chart has this chart whole.
Turning it on
Each connection’s index is built the first time something needs it. QuBe Sync reads the names in the company file’s lists from QuickBooks itself, a step at a time, as the Web Connector checks in. Your own requests always go first, and those reads never appear among your requests. It takes a few Web Connector sessions, more on a large company file.1
Opt in, with a webhook_url
Opting in starts the build ahead of your first match:It answers
202 with the connection’s name_index while the index builds, or 200 if it’s already
ready, and then nothing is sent. Opting in again while it builds replaces the webhook_url, so it’s
safe to repeat. A first match does the same as opting in, without the webhook: it’s refused with
name_index_building, and starts the build.2
Get name_index.ready
When every list is in, QuBe Sync sends your If QuickBooks’ answers to one step are lost three times in a row (the session ended, or the Web
Connector reported an error), the build gives up and you get
webhook_url this, signed like every other
webhook:name_index.ready
name_index.failed instead. It starts
again on its own a day later while the connection is matching, or at your next match or opt-in.3
Match
Every match from then on is answered from the index.
GET /connections/{connection_id}):
object
state is ready, building, not_built (nothing has needed matching yet) or unavailable (a
standard connection). as_of is when it last caught up with QuickBooks, and records how many records
it holds.How current it is
index.as_of in every answer says when the index last caught up with QuickBooks. A record added, renamed
or deleted in QuickBooks itself after that isn’t reflected yet.
What you add, rename or delete through QuBe Sync is. When QuickBooks answers one of your requests, or a
workflow run’s, that adds or changes a record in a list the index holds (POST /customers, an
ItemServiceAddRq, a ListDelRq), the record goes into the index before the answer reaches your webhook or
the run’s next step. So a run that creates an item, followed seconds later by one that names it, finds it as
exact. A request that asks for some fields only (include_ret_element) isn’t taken this way; the next
match refreshes instead.
Errors
See Error handling for the shapes, and the
API reference for both endpoints.

