- you already have an API to call
- the URL is a deployment choice, not something to hardcode
- you want no code of your own to write or review
webhook: tool works on livekit and pipecat. The twilio target refuses it:
move the request into a Python handler. It is also
refused on an slng target: SLNG owns a tool’s code, version and gate pipeline, so there is
nowhere in a package for a URL and its credential to write to there.
Reference a tool your organisation already has on the platform with
slng: instead.
On this page:
- The block - the four keys
- The path renders per call - variables in the URL
- Authentication - bearer or API key
- What the model fills in, and what it cannot - input against with
- Advanced - naming the base URL, describing the result
- Troubleshooting - the refusals, and their fixes
tools/confirm_appointment.yaml
url_env and base_url this way compiles to livekit and pipecat; an slng
target refuses it, for the reason in the note above. examples/hotel-concierge
ships no webhook tool: its places-search tool reaches SLNG through the
hosted-tool block instead.
start: say: on the tool’s entry in agent.yaml is the one line worth knowing
about here, because a webhook is the tool most likely to keep the caller
waiting:
agent.yaml
{{variables}} are
refused. Full rules in the behavior fields.
The block
string
An UPPER_SNAKE environment variable name holding the base URL. Required on LiveKit and
Pipecat. At least one of
url_env and base_url must be present; no URL is inferred.string
A literal HTTPS base URL. Omit it on code targets, which read
url_env instead. The
SLNG target refuses authored webhooks: publish the tool on SLNG and use a hosted slng
reference.string
Path appended to the base URL. A non-empty path starts with
/ and may use
{{state.variable}} tokens, whose values are URL-encoded. Omit to use the base URL alone.object
Authentication using the fields below. Omit to add no authentication header.
url_env holds a name, so the base URL is a deployment choice rather than a
package one: staging and production run the same package against different APIs.
Pipecat and LiveKit read it at run time and never look at base_url.
The path renders per call
{{state.customer_id}} is a variable, rendered when the tool is
called, and the rendered value is URL-encoded for you. Because it renders per
call rather than once at session start, a variable that only gets its value
once a task saves it partway through the call is fine here.
If the variable has no value when the model calls the tool, the call is refused
and the model is told what to ask the caller for, rather than sending a
half-formed request.
Authentication
string
required
Accepts
bearer or api_key. Required when auth is present; no scheme is inferred.UPPER_SNAKE name
required
The environment variable holding the token. Always a name, never a value.
header name
default:"X-API-Key"
Legal on
api_key only.agent.yaml under
secrets:. The compiler also infers these fields as
required environment names, so leaving one out of secrets: warns without
dropping it from generated environment instructions or checks.
api_key and its header reach livekit and pipecat exactly as written. An
slng target never sees this field at all: webhook: is refused there, so
there is no bearer-only rewrite to plan around any more.
What the model fills in, and what it cannot
input is the model’s half of the request. with is yours.
with: are merged into the call and never shown to the model, so it
can neither see them nor overwrite them. That is the place a customer id, a
captured slot, or the number the call went out to rides along.
The tool above takes no parameters at all: everything the API needs comes from with:.
The model can call it or not call it, and that is the whole of its authority. It
cannot invent a customer id.
A value that is exactly one state.x or {{token}} keeps that field’s type from state.py, so a
number stays a number.
Every with: value must be a scalar. Strings, numbers, booleans, and null are
legal; maps and lists are refused.
Advanced
Naming the base URL
base_url exists for a hosted target: that is where SLNG used to store the
URL in the tool body it pushed, since there was no environment for it to read
at run time there. An slng target refuses a webhook: block outright now,
for the reason at the top of this page, so there is no longer a target that
reads base_url. Name url_env for livekit and pipecat, which read it at
run time and never look at base_url.
Describing the result
output is optional author documentation. The compiler checks that it is a
JSON Schema object, but no generator sends it to the model or writes it to the
compile report, and nothing checks the API’s response against it either. The
example records the shape the API is expected to return; teach the model how
to use that data in the tool description or prompt.
A local: handler’s output: was the one exception,
checked on an slng target because SLNG ran that handler itself. That block is
refused there now, and the check moved with the code: a hosted code tool’s
module carries its own Output model, and SLNG reads the result shape off
that. See Python tools
and Hosted tools.
Troubleshooting
Both code targets refuse a tool with no url_env
Drop url_env and both code targets refuse, and the message still names a
hosted target as the reason the field exists at all:
url_env with the UPPER_SNAKE name of the variable that holds the
base URL, and keep base_url if the package also has a hosted build.
A {{token}} in the path names nothing
A token that names nothing at all fails at compile time:
State in state.py, or correct the spelling in the
path.
token_env holds a value instead of a name
Again a name, never a value. A token written in place of a name is refused:
An auth field belongs to the other scheme
A field from the other scheme is refused too, and the message says why:
header from a bearer block, or switch type to api_key if
the API really wants a named header.
A key is in both input and with
A key cannot be in both lists, and the refusal explains the reason rather than
just the rule:
with: for a value the model must
never see, or in input: for one the model fills in.
Where to go next
Python tools
When the request needs code of your own, like a signature.
Variables
Where
{{state.customer_id}} comes from.