A tool's input schema describes the data AGENTO accepts. It does not send those fields to your API automatically. request.map is the explicit bridge between validated values and the REST endpoint.
Read your first tool first. This guide shows where each value comes from, where it goes, and what happens if it is absent.
Start with the request your API expects
Suppose your API accepts:
GET /services/svc-color/slots?date=2026-10-01&limit=5
X-Locale: ar
Your application supplies service_id, date, and optional limit. The names on the left of each mapping belong to the HTTP contract; paths on the right read the tool input.
Complete tool entry; place it under tools in config.yml:
- id: find-slots
tool:
description: Find available appointment slots for a service and date.
input_schema:
type: object
properties:
service_id: { type: string, minLength: 1 }
date: { type: string, format: date }
limit: { type: integer, minimum: 1, maximum: 20, default: 5 }
required: [service_id, date]
request:
method: GET
url: https://api.example.com/services/{service}/slots
map:
path:
service: $.service_id
query:
date: $.date
limit: $.limit
headers:
X-Locale:
source: constant
value: ar
For { service_id: "svc-color", date: "2026-10-01" }, validation inserts limit: 5. Mapping replaces {service}, adds the two query parameters, and writes the header. The model never chooses the hostname or constructs this URL.
Try changing limit to "5". It fails schema validation: AGENTO does not coerce strings into numbers. Try removing service_id: the runtime asks for missing input before sending HTTP.
Understand the four destinations
| Destination | Use it for | What changes at execution |
|---|---|---|
request.map.path | URL segments such as a service ID | Replaces declared {placeholders} with encoded scalar values. Missing values leave an unresolved placeholder and stop execution. |
request.map.query | Search terms, dates, filters, pagination | Adds URL parameters. An array becomes repeated parameters, such as tag=a&tag=b. |
request.map.body | JSON fields for POST, PUT, PATCH, or DELETE | Builds a JSON object from mapped fields. GET and HEAD bodies are rejected at compilation. |
request.map.headers | Non-secret metadata such as locale | Converts scalar values to strings. Credentials and reserved transport headers are rejected; use request.auth. |
A body mapping is a map of top-level keys. To send a nested object, map the entire declared object to a body key; dotted destination keys are not a nested-object construction language.
# Fragment inside request.map of a confirmed write tool.
body:
customer: $.customer
channel:
source: constant
value: web
With input { customer: { name: "Maya" } }, the body is {"customer":{"name":"Maya"},"channel":"web"}. Declare customer and its shape in tool.input_schema, and configure effects and confirmation for the write.
Choose the source deliberately
$.date is shorthand for { source: tool-input, path: $.date }. A dollar path is always relative to the selected source; it is not a global variable.
| Source | Root of its path | When to use it |
|---|---|---|
tool-input | Current tool arguments after validation and dependency resolution | User-owned values, plus values explicitly filled by dependencies. |
session | Host-provided context | Tenant, locale, or other trusted application context. It does not expose the session's internal history or credentials. |
dependency | Resolved data for the named direct dependency | A value returned by a declared lookup. After a selection, this is the selected item, not the original response envelope. |
constant | Its literal value | A fixed currency or application channel. A bare string such as SAR is not a valid value source. |
generated | A newly generated UUID | Request correlation. This is generated during request preparation, not a substitute for API idempotency support. |
selection | Declared selection display facts | Confirmation previews only. It is forbidden in HTTP and dependency argument mappings. |
Example fragments:
# Under request.map.body of a confirmed write tool:
tenant_id: { source: session, path: $.tenantId }
currency: { source: constant, value: SAR }
request_id: { source: generated, generator: uuid }
# Under request.map.body when availability is a declared dependency:
slot_token:
source: dependency
tool: availability
path: $.slot_token
Supply session context from your server:
const result = await runtime.invoke({
sessionId: authenticatedConversationId,
tool: "book",
arguments: { date: "2026-10-01" },
context: { tenantId: authenticatedTenantId },
auth: { token: userAccessToken },
});
The identifiers in this host snippet come from your authenticated application. Do not copy a tenant ID from untrusted chat text. Credentials belong in auth, never in context mappings or the tool schema.
Paths are property access, not expressions
Supported: $, $.id, $.customer.address.city, and numeric property segments where the source permits them. Unsupported: $.items[*], $.items[0], filters, interpolation, function calls, and JavaScript expressions.
For tool-input mappings, the compiler also checks that the path exists in the declared schema. A path that is syntactically valid can still be rejected because it points at an undeclared property. Prototype-related segments are forbidden.
Use depends_on.select to choose from a list. Do not approximate selection by guessing an array index in a mapping.
Missing is different from empty
An absent optional tool-input value is omitted from the mapped object. An absent session or dependency source raises INPUT_MAPPING_MISSING; the runtime does not silently invent a tenant or identifier. A literal null is a value, not “missing”; it can be sent in a JSON body but is invalid for a URL or header.
Path and header values must be scalar. Path values cannot contain slashes, backslashes, control characters, or the special segments . and ... Header values cannot contain line breaks. Query arrays must contain scalar members.
Do not confuse the three maps
# Fragment inside a dependency on services:
arguments:
q: $.service_query
map:
$.service_id: $.id
This is a separate fragment inside the parent tool’s request, not a continuation of the dependency block above:
map:
body:
service: $.service_id
argumentssends the parent'sservice_queryinto the lookup's argumentq.- The dependency's
mapcopies the lookup result'sidinto the parent'sservice_id. Its left side is a destination path. request.map.bodysends that resolved value as the API fieldservice. Its left side is a field name.
This distinction is the foundation of dependencies and selection.
Verify the boundary
Compare the request reaching your staging API with the direct request you know works. Inspect URL, method, and field names without logging credentials. You can then capture this behavior with a mocked transport in automated tests. Test an absent optional field, missing context, invalid types, and encoded path characters. The complete booking example gives you a runnable fixture and its expected sequence.