The customer lookup contract
Before oHallo can verify a caller, link a conversation to a customer, or send a record-bound verification link, it has to find the customer in your own system. That lookup runs first, on every inbound message and every call. This page is the contract for the tool that answers it.
Your customers live in your systems, not in oHallo. oHallo reads a customer from those systems when it needs one and stores only your customer reference. The operator sets up, on the connection’s page under Settings, Customer records, in the rows By email address and By phone number under Finding customers, which of your tools finds a customer, which tool input receives the email address or the phone number, which field in the answer holds your customer reference, and which field in the same tool’s answer holds the email address or the phone number itself: National eID checks the answer by it, the email link and the email sender address tie a customer to their own record through the field with the email address, and Caller ID through the field with the phone number. The platform then calls exactly what was designated, with exactly the argument name you declared, and nothing else.
A tool that returns one of your records, such as an order, can carry an email link too: the field in its answer that holds the email address, and optionally your customer reference, set up under Customer data with Add an email link. In chat, a request to see that record on a tool declared A customer and set to identified then sends the email link to the address the record holds, and pressing it ties the customer to that record.
The contract
Section titled “The contract”A lookup tool needs four things:
- An input for an email address, an input for a phone number, or both.
The names are yours; the operator designates them. The designation is
validated against your declared
inputSchema, so declare one. - A stable customer reference in the response. The identifier your own
systems use for this customer, at a field path the operator can designate.
It must not change when the customer renews, moves, or changes contact
details. Return it as a string, or as a JSON number that is a whole number
from 0 to 9007199254740991. It is your own customer number, never a
personal identity number: oHallo does not keep a reference that has the
form of a Danish CPR number, a Swedish personnummer or coordination number,
or a Norwegian fødselsnummer or D-number, with or without separators, or
that equals the person identifier or the national identity number in the
same answer, where the operator designated those fields on the same tool;
the comparison ignores separators, letter case and the leading zeros of a
value made of digits only. A JSON number outside that range is not kept
either. In each of these cases the customer is still recognised, no
reference is stored, and the lookup records
customer_ref_refused. Its field is never the field that holds the email address, the phone number or the national identity number on the same tool: oHallo refuses that designation. - A declared
outputSchema. The designation surface derives the pickable fields from it. A tool without one can still be designated, but the operator has to type field paths by hand and a typo only surfaces at runtime. - A clear answer when the person is unknown. Answer with a result marked
isErrorwhose first content item is a text item holding JSON with"error_type": "not_found", or return an empty object. The platform records either as “your system does not know this person”, which is a normal outcome, not a fault. Any other answer, one that is not empty and carries neither your customer reference nornot_found, cannot be read: on email, where the lookup by the sender’s address finds the record a National eID verification link is compared with, and the record the email link and the email sender address tie the sender to, no link is issued and no one is tied; on a call, Caller ID ties no one. When more than one of your customers holds the email address or phone number, return every match as a list in place of the single record: the designated path then holds no single customer reference, so oHallo keeps none for that address or number, starts no National eID verification through it and ties no one to it.
A worked example
Section titled “A worked example”The tool declaration:
{ "name": "find_contact", "description": "Find a contact by email address or phone number.", "inputSchema": { "type": "object", "properties": { "email": { "type": "string" }, "phone": { "type": "string" } } }, "outputSchema": { "type": "object", "properties": { "contact_id": { "type": "string" }, "account_id": { "type": "string" }, "name": { "type": "string" }, "email": { "type": "string" }, "phone": { "type": "string" } }, "required": ["contact_id", "name", "email"] }}What the platform sends when a new caller phones in, assuming the operator
designated phone as the phone input, and email and phone as the fields
that hold the email address and the phone number:
{ "phone": "+447700900123", "tenantId": "...", "workspaceId": "..." }What your tool answers:
{ "contact_id": "CON-001", "account_id": "ACC-001", "name": "Alex Doe", "email": "alex.doe@example.com", "phone": "+44 7700 900123"}The platform reads the designated reference field (contact_id here), stores
it as the contact’s external reference, and discards the rest of the payload
apart from name and company fields used for the operator’s screen. The answer
never reaches a language model. For National eID it also reads the designated
phone field, which holds the number it sent: +44 7700 900123 and
+447700900123 are the same number.
The company account and the owner fields
Section titled “The company account and the owner fields”For a business customer the operator can also designate, on the lookup the
National eID rows use, the field that holds the company account number: your
own number for the company the customer belongs to. When the customer verifies,
oHallo reads it from the same answer the customer was proven against and ties the
customer to that one company account, beside their customer reference. Return
one value per record, as a string or a JSON number with the same rules as the
customer reference. A list, an object, an empty value or a value in the form of
a personal identity number ties no company, and the lookup records
account_ambiguous. A record that names no company ties none. When your lookup
returns the company’s own record, the company account number may be the same
field as the customer reference.
Beside it the operator can designate the field that holds that company’s
registration number, in Denmark the eight-digit CVR number. oHallo compares
the company number of a MitID Erhverv login with it and, on a match, ties the
employee to the company account number read from the same answer, never to a
customer reference. Return one value per record, as a string or a JSON number;
spaces, dots, hyphens and a leading DK are ignored. oHallo keeps neither
registration number, the record’s or the login’s: it compares keyed hashes and
discards the values. The company account number is kept as the proven tie. A
record that names no company account number ties no company, whatever
registration number it holds.
The tools that return your records name their owner the same way. Each record
of a tool declared at the customer level carries, at the owner field the
operator declares, the customer reference this lookup returns (contact_id in
the example above), and the company account number when it names one
(account_id). Each record of a tool declared at the company level carries
the company account number at its owner field, and a field that names the
customer who placed it, declared as another party. A tool that changes
something, or that returns a list, takes an input that receives the number:
oHallo fills it with the verified customer’s own customer reference, or their
company’s account number, on every call, and your tool answers about that
customer only. Answer a record of another customer exactly as one that does not
exist (not_found). An error about the caller’s own record, such as an order
that has already shipped, names the record’s owner like any other answer, or it
is withheld:
{ "error_type": "order_not_eligible", "reason": "Order ORD-001 cannot be cancelled: it is already delivered", "order_id": "ORD-001", "contact_id": "CON-001", "account_id": "ACC-001"}oHallo’s own identifiers never reach your tools: a tool receives tenantId and
workspaceId and the arguments the request made. The lookup is read to find
and verify customers whether or not the operator declares its scope; an agent
calls it, like any tool, only once it is declared.
The lookup a request on chat reaches
Section titled “The lookup a request on chat reaches”On chat, a request to a tool declared at the customer or company level that
names a record, such as cancel_order with an order_id, is verified against
the record a lookup on the same connection finds from the request’s own
arguments. The lookup is the requested tool itself when the operator set the
National eID rows up on it, or else the one tool on that connection with those
rows that takes one of the request’s arguments. oHallo calls it with only the
arguments it declares in its inputSchema, and matches an argument name to an
input whatever its letter case and underscores, so orderId reaches
order_id. When two such tools take the request’s arguments, or none does, no
lookup is called and a colleague takes over the request.
Declare each lookup’s inputs. A tool that declares no input properties takes
any argument, so beside another lookup that takes the request’s argument
neither is chosen. With find_customer taking email and phone, and
get_order taking order_id, a request to cancel_order with an order_id
is verified through get_order.
What the platform sends
Section titled “What the platform sends”- Email: the sender address as it arrived on the channel.
- Phone: the caller’s number as the carrier delivered it, in E.164 form,
for example
+447700900123.
Match an email address that is identical apart from the case of the letters A to Z: every other character as sent, in the local part and the domain. Match a phone number in any written form: any spacing, punctuation, international or national form. Answer only the records that hold that exact address, or that number with all of its digits.
The value in the answer
Section titled “The value in the answer”For National eID, and for the email link, the email sender address and Caller ID
where they tie a customer to their own record, the operator designates, on the
lookup’s own row (By email address, By phone number), the field in the answer that holds the email address or the
phone number, and
oHallo uses the record only when that field holds the value it looked up: the
same address, identical apart from the case of the letters A to Z, with
surrounding spaces or a display name; or the same number in any written form. A
number that begins with + or 00 names its own country. Any other number is read
in the country of the workspace’s eID connection, and only where it is no valid
number there do its digits alone decide, so 4612345678 is +46 12 34 56 78 and
49 33 62 52 is the Danish +45 49 33 62 52. For Caller ID on a workspace whose
eID connections name no single country, only a number that begins with + or 00
ties a caller. A field may hold a list of addresses or numbers, of which one
must be the value looked up. When a record holds several addresses or numbers in
separate fields, designate the field your tool matches on, so the record your
tool returns holds the value there.
Risk classification
Section titled “Risk classification”The operator classifies every tool’s risk in the MCP hub. A lookup tool is
typically read_pii, because it returns personal data. The resolution read
runs on a server-side reader lane that satisfies the assurance gate for
designated tools, so a read_pii classification does not block the lookup.
Two rules follow from the lane being read-only:
- A tool classified
writeordestructiveis refused as a lookup. Keep the lookup a pure read. - A tool with no risk classification at all is treated as a write and refused
too. Classify it
readorread_pii.
Side effects and idempotency
Section titled “Side effects and idempotency”The lookup carries no idempotency key and takes no dispatch claim, because it must be free of side effects. The platform may call it repeatedly for the same person. Do not create records, send notifications, or mutate state in a lookup tool.
When something goes wrong
Section titled “When something goes wrong”The connection’s section under Settings, Customer records shows the state of the lookup in plain language, including the latest outcome recorded against the designated tool:
| Outcome | Meaning |
|---|---|
resolved | Your tool answered with a customer reference. |
not_found | Your tool answered, and does not know this person. Normal. |
no_designation | No lookup is designated for this channel yet. |
tool_not_found | The designated tool is no longer offered by the connection. |
field_missing | The answer carried no value at a designated field: the reference, or for National eID the field that holds the email address or phone number. |
customer_ref_refused | The value at the designated reference path is not kept as a customer reference (see item 2 above). The customer is recognised without it. Designate the field that holds your own customer number. |
account_ambiguous | A National eID verification read the record, and the field designated for the customer’s company account number did not hold one account: a list or an object, an empty value, or a value with the form of a personal identity number. No company is tied to the customer. Designate the field that holds the one account the customer belongs to. |
record_mismatch | A National eID verification looked a customer up by email address or phone number, and the field designated for that value held another address or number. The verification leaves that record aside. Designate the field that holds the value your tool matches on, and answer only records that hold it. |
lookup_gated | Platform policy refused the read. Usually the tool’s risk classification: classify it read or read_pii. |
upstream_error | Your system returned an error or timed out. |
Without a working lookup, the assistant cannot recognise a returning customer and cannot send a verification link to the address on your record. The lookup is the foundation the rest of identity builds on.