You are going to build a campaign that calls a list of leads, opens by saying who is calling and why, asks four qualifying questions, writes the answer back to your CRM, and leaves behind a call record you can filter into a shortlist. When it is finished you will have one published agent, one published tool, one outbound-capable number, and a batch that dials on a schedule you set, retries the phones nobody answered, and stops itself if something is broken.
Two things decide whether this campaign is a good idea, and both are easier to accept now than from an invoice.
  • Answering machines are not detected. There is no answering-machine detection anywhere on Talqing. A call that reaches voicemail is answered, billed at the full platform fee, and reported as completed — and because it completed, that recipient is never retried. Your agent will talk to voicemail boxes and your results will contain calls that reached nobody.
  • Every call in a batch runs your agent’s current published version. The batch re-reads it before each dial, so republishing at 14:00 changes every call placed from 14:00 onwards, including the rest of a campaign that has been running since morning. If you need a campaign held steady across an edit, pin it — see batch calling.

What you need

Everything below is regional. An agent, a number, a batch and a credit balance created against https://api.in.talqing.com do not exist in https://api.us.talqing.com; the same token reaches both. See regions.

The build

1

Write the agent for an outbound conversation

An outbound call is a stranger’s phone ringing. The prompt has to carry three things a support agent’s prompt does not: who is calling, why, and how the person gets off the phone without a fight.
Writes replace the whole config, so read the current one and send it back changed rather than sending the field you want to alter. See agents overview.
2

Personalize each call with the recipient's own userdata

Each row of a batch carries to plus a flat map of strings that becomes that call’s session state, and {{userdata.field}} in the prompt and the greeting is substituted from it when the agent starts. One published agent, a different opening line per lead.
A field that is not in userdata resolves to nothing, so put the token where an empty value still leaves a sentence. "Hi {{userdata.first_name}} — this is Maya from Northwind Tools" still reads if the name is missing; "Hi, am I speaking to {{userdata.first_name}}?" does not. Values are strings on purpose — a spreadsheet cell reading 007 is a reference number, not the integer seven. See userdata.

Branching the opening on the call direction

If the same agent also answers inbound calls on that number, the greeting has to differ: outbound says who is calling and why, inbound does not. A greeting is one verbatim line and has no conditionals, so put the branch in an on_enter hook, which finishes before the greeting and whose writes to userdata the greeting then reads.Create this tool, publish it, and set the agent’s greeting to "{{userdata.opening}}":
Attach it as "on_enter": { "tool_id": "7a2e5b91-0c34-4d6f-8a12-b93f5c7e2d61" }.
Keep this hook to set_variable operations and nothing else. If the hook fails, the call carries on but {{userdata.opening}} resolves to nothing — and an empty greeting on first entry means the agent says nothing at all and waits for the other person to speak. On an outbound call that is dead air into a stranger’s ear. A hook that only writes two strings has nothing that can fail; a hook that calls an HTTP endpoint does.
{{system_vars.direction}} is inbound or outbound and is empty on web calls and text conversations. See variables.
3

Write the qualification back to your CRM

One tool, one capability. The model supplies what it learned as arguments; the tree posts them and says one line. The CRM’s token is a workspace secret, so the model never sees it and cannot put it in an argument.
Two details worth understanding. silent: true on the http operation hides the CRM’s response from the model — and because no operation in the tree can then hand the model anything, the tool is derived silent: the say line plays and the agent does not improvise a second sentence on top of it. And {{system_vars.now}} and {{system_vars.agent_phone_number}} are filled in by the platform, so the model is never asked to know the time or the number it called from. See the operation tree and templating.Attach it with "tools": [{ "tool_id": "3f8b0f4c-9a1e-4a7d-9c2b-1d5e6f70a821" }] and leave tool_version out — publishing the agent fills it in.
4

Turn on post-call analysis

The tool writes what the agent was told. Analysis writes what a reader infers from the finished transcript, which is what makes a campaign readable without listening to four hundred calls. Add it to the agent’s config:
Analysis fields and userdata answer different questions, and one must not be used for the other. userdata is what the agent knew — seeded from the recipient row, written by a tool, confirmed on the call. Analysis fields are what a model inferred afterwards by reading the transcript. Analysis results are never merged into userdata, deliberately: a guess must not overwrite a tool-confirmed value.So budget_band on the CRM write is an argument the model filled in while the person was on the line, and it is what you act on. budget in the analysis fields is a sentence a reader can scan. If a value has to be right, make it a tool argument. If it only has to be readable, make it an analysis field.
Analysis is voice and video only and runs on the agent’s own LLM, so it needs no second provider key. It adds roughly 5% to the cost of a call and runs during call finalize, so its tokens land in the same bill. A call is skipped rather than analysed when it ended on a failure reason, when fewer than two caller turns happened, or when the spec produces nothing. See call analysis.
5

Publish the agent

Publishing freezes the draft as an immutable version and pins each attached tool to the tool version live at that moment. Republishing the tool afterwards changes nothing until you publish the agent again.
6

Pick the number you dial from, and check its outbound flag

A batch dials from one number, named by from_phone_number_id. That number has to be active on a carrier account that is ready, and it has to have can_outbound set — a number imported for inbound only is refused at create with this number is not enabled for outbound.
The number the campaign dials from is also what decides how each to is parsed: numbers are normalized against the region of the dialling number, not a global default.
7

Place one real call to yourself

Before the list, one call. POST /v1/calls/outbound runs the same published agent with the same userdata a batch row would carry, so it exercises the greeting, the tool and the analysis in one go.
This rings a real phone and is billed. There is no dry run. Use a number you own, and use a crm_id your CRM will accept and you can delete afterwards.
8

Create the batch

Now the list. One request carries the policy and up to 10,000 recipients.
In the dashboard this is Telephony → Outbound Calling → New batch, which takes the same fields and a pasted or uploaded list.The four numbers that decide how it behaves:timezone is the batch’s own clock, and it interprets calling_window — so the window is the recipients’ working day, not the working day of whoever pressed the button.

Test it

Before the list runs, prove each piece separately on the single call you placed.
  1. The transcript. GET /v1/calls/{session_id} returns the transcript with every tool call and its output, plus per-stage latency. Check the greeting was substituted, that the agent gave the way out inside the first three sentences, and that record_qualification was called once rather than twice.
  2. The CRM row. Look in your CRM, not at the tool’s response. The http operation is silent, so a 200 with the wrong body looks identical to a correct write from the agent’s side.
  3. The analysis. Read analysis.status, analysis.outcome and analysis.fields on the same call. If a field came back vague, that is the field’s description failing, not the model — rewrite it and use POST /v1/calls/{session_id}/analysis/preview to try new wording against the same transcript. Preview writes nothing and still costs a real LLM call.
  4. A two-row batch to phones you own. Create the campaign with two of your own numbers and max_concurrency: 1 before you create it with four hundred. Everything about scheduling, retries and counters is the same shape at two rows as at four thousand.

Running it

next_dial_at is the field that tells “waiting for Monday” apart from “stuck”. It is null while the batch is actually dialling, while a person has it paused, and once it is over — all three of which status already tells you.
  • Pausing stops claiming new recipients; calls already ringing or connected run to their end. Resuming carries on, and a batch whose start_at is still in the future goes back to scheduled rather than running — pausing and resuming is never a way to start early.
  • Adding rows to a running batch works: POST /v1/calls/batches/{batch_id}/recipients appends, and numbers the batch already holds are silently skipped and reported rather than rejected. Re-uploading the same CRM export never calls anyone twice.
  • The circuit breaker stops a batch after ten consecutive setup-style failures — a carrier fault, an unpublished agent, an expired credential. The count is rolling and any success resets it, and busy signals and unanswered phones never count towards it: a run of those is a phone list, not a fault.
  • Running out of credit pauses the campaign rather than failing it. No attempt is spent on anybody, failure_reason says the workspace is out of credits, and it resumes on its own once you top up. There is no button to press.
  • Cancel is final and there is no delete. A batch is the record of money spent.
The full behaviour of every one of these — statuses, the retry table by close reason, policy edits — is in batch calling.

What to watch

Turning the campaign into a qualified list, then, is two filters and a fetch: list the batch’s calls with outcome=success, fetch each one, and keep the rows whose fit is strong or possible. The next_step field is what a salesperson reads first.

Where it falls short

  • No answering-machine detection. Say it again because it shapes your numbers: voicemail is a completed, billed, never-retried call, and some fraction of your completed count is machines. Nothing on the platform can tell you which.
  • One batch is one agent, one number, one clock. There are no per-recipient overrides of anything but userdata. Half a list needing a different agent, a different caller ID or a different language is a second batch.
  • The batch runs as the person who created it. Every dialling pass re-authorizes as that user. If they leave the workspace or drop below the EDITOR role, their campaign records a configuration failure on the next pass, and ten of those trip the breaker. Hand running campaigns over before somebody leaves.
  • Calling law is yours to know. Consent, do-not-call registers, permitted calling hours, disclosure requirements and recording law differ by country and by state, and Talqing enforces none of them. calling_window is a scheduling feature, not a compliance one — it does not know a public holiday from a Tuesday.
  • Analysis is a reading, not a record. It is one LLM call over the transcript, with a 30-second timeout, three-valued on purpose so that a wrong number comes back unknown rather than being forced into a verdict. Back a business decision with what a tool confirmed, not with what a summary suggested.

Next

Batch calling

Every status, the retry table, policy edits and pinning a version.

Call analysis

Outcome, fields, preview and backfilling calls you already have.

Webhooks

Get each finished call, with its analysis, pushed to your own system.