All pages

Guides

Inbound calls

Tell an agent who is calling — your server is asked while the phone is answered, and the values reach the agent before its first reply.

View as Markdown

When somebody calls one of your agents, the agent can know who they are before it says a word beyond hello. Give the agent a context lookup: an address on your own server that is asked about each call as it is answered, and answers with values for the agent's variables.

Set it up

In the console, open the agent's Connect → Phone tab and fill in Look up who is calling:

  • Live address — asked by real calls. It must be https:// and reachable from the internet.
  • Test address — asked by test calls instead, never by real ones.
  • Time limit — how long your server has to answer: 1,000 ms unless you change it, at most 2,500.
  • Sample answer — what a test call uses when there is no test address, so you can try the agent before your server exists.

Saving shows each address's signing secret once. Keep them on your server.

What your server is sent

A POST with the same headers a webhook carries, signed with the lookup's own secret:

{
  "event": "conversation.context",
  "created_at": "2026-10-07T09:14:03.120394+00:00",
  "data": {
    "conversation_id": "5f0c9a52-7d3e-4b1a-9c8f-6e2d4a0b3c1d",
    "livemode": true,
    "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
    "project_id": "0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
    "channel": "phone",
    "direction": "inbound",
    "from_number": "+12025550123",
    "caller": "+12025550123",
    "to_number": "+12025550199",
    "phone_number_id": "9e8d7c6b-5a4f-3e2d-1c0b-a9f8e7d6c5b4"
  }
}

Check the signature before you trust it — the webhook helper checks a lookup too, with the lookup's secret.

What it answers

200 with JSON, at most 16 KB:

{"variables": {"first_name": "Sara", "plan": "gold"}, "metadata": {"crm_id": "C-1042"}}

variables fill the agent's fields and are checked like any others. metadata is yours, up to 20 keys, and is kept on the conversation. Answer {"variables": {}} for a caller you do not know.

When it is asked, and who waits

The lookup starts the moment the call is answered, and your server answers while the greeting plays — so an answer within the time limit costs the caller nothing. Only two things wait:

  • A greeting that names a value, like Hello {{first_name}}, waits for your answer — at most the time limit — so it can say it. The console's greeting editor says so when this is the case.
  • A caller who talks before the greeting ends waits, at most the time limit, for the values to arrive before the agent answers.

The values are in the agent's instructions before its first reply either way.

When it fails

A slow or broken server never stops a call: the agent answers without the values. Nothing is retried — somebody is on the line. The conversation's context says what happened:

context.status Means
pending Your server is still being asked.
ok The values were used.
timeout No complete answer within the time limit.
bad_status Your server answered something other than 200.
too_large The answer was over 16 KB.
invalid_answer The answer was not the JSON above, or a value did not fit — problems names each.
refused The address is not one the platform will send to.
failed The request could not be made.
busy Your organization had too many lookups waiting at once.

context.ms is how long the lookup took, and applied_before_first_reply whether the values were in time for the first reply.

Fill fields from the phonebook

For values that rarely change — a name, a plan, a member number — you need no server at all. Turn on Fill fields from the phonebook on the agent's Connect → Phone tab, and keep the project's phonebook in step with the contacts API. When a call arrives, the entry with the caller's number fills the agent's fields before the call is answered, with the source phonebook.

Only the agent's own fields are filled, and only with values that fit them. When the agent also has a lookup, the lookup's answer replaces what the phonebook said, since it is newer.

Details a call brings with it

A phone system that transfers callers to your agents can send what it already knows in X- headers on the call. Name each header and the field it fills on the trunk's Settings in the console — Fields from SIP headers, like X-Customer-Id=customer_id — and the agent that answers starts with those values, with the source sip_header.

Only the headers you name are read, and a value that does not fit the field is left out rather than refusing the call. They are the call's own values: they replace what the phonebook says, and the lookup's answer does not replace them.

A caller ID is not proof

The number a call comes from is whatever the network was told, and anybody can set it. Use the lookup and the phonebook to personalise a call — the name, the plan, the last order — never to decide that the caller is who the number says. Values from either are never treated as the caller's own answers, and an agent that needs to know who it is talking to should ask, the way a person would.

In test mode

A test call to your agent asks its test address, or uses its sample answer, and never the live one. Make one from a test number with a test key:

Make a test call to an agent
#!/bin/sh
# Make a test call to an agent. Test keys only. It asks the agent's test lookup and plays a scripted call.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/test/inbound-calls" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  --json '{
  "agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
  "from_number": "+12025550100"
}'

The answer says what the lookup made of it — where the values came from, what was accepted and ignored, and the greeting they produced — and the call then plays out like any test call. With aigently listen and --forward-lookups-to, the lookup reaches your own machine.