> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rumik.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# variables and tools

> fill an agent's prompt from your own systems, and let it call them.

a **variable** is a `{name}` in a prompt or greeting with a default value. a
**tool** is an http endpoint of yours: a `before_call` tool runs at the start
of every call and fills variables from its response; a `during_call` tool is
a function the model can invoke mid-conversation. both are account-wide — one
list, shared by every agent.

## variables

`GET /v1/variables` · `POST /v1/variables` · `GET | PATCH | DELETE /v1/variables/{id}`

typing `{company}` into an agent's prompt declares a variable named `company`
(blank default) if none exists. declare it yourself to set the default:

<ParamField body="name" type="string" required>
  `^[A-Za-z][A-Za-z0-9_]*$`, up to 64 characters, unique on the account
  (case-insensitive).
</ParamField>

<ParamField body="defaultValue" type="string">
  used when no tool supplies a value, or the tool fails. up to 2000
  characters; blank by default.
</ParamField>

<ParamField body="toolId" type="string">
  a `before_call` tool whose response fills this variable at the start of each
  call.
</ParamField>

<ParamField body="toolOutput" type="string">
  which of that tool's `outputs` to take. omit for a tool with one output, or
  one named like the variable.
</ParamField>

```bash curl theme={null}
curl -X POST https://silk-api.rumik.ai/v1/variables \
  -H "Authorization: Bearer rk_live_•••••••••" \
  -H "Content-Type: application/json" \
  -d '{ "name": "company", "defaultValue": "acme" }'
```

```json theme={null}
{
  "id": "019f9f43-5e71-7082-bd9e-0f1a2b3c4d5e",
  "name": "company",
  "defaultValue": "acme",
  "toolId": null,
  "toolOutput": null,
  "createdAt": "2026-09-11T10:00:00Z",
  "updatedAt": "2026-09-11T10:00:00Z"
}
```

`PATCH` takes any subset; `"toolId": null` disconnects the tool. `DELETE` is
refused with `409 agent_variable_in_use` while any agent's live prompt,
greeting or draft still spells `{name}` — remove it from the prompt first.
up to 200 variables per account.

## tools

`GET /v1/tools` · `POST /v1/tools` · `GET | PATCH | DELETE /v1/tools/{id}`

`GET /v1/tools` returns your tools as `items`, the platform's built-ins as
`defaults` (`end_call` lets the agent hang up), and `contextParams` — the
tokens every tool can use: `{phone_number}` (the other end of the call),
`{user_id}` (your account), `{agent_id}` and `{call_id}` (the call's id, the
same one `POST /v1/calls` and `GET /v1/calls` show). a before-call tool
receives `{call_id}` when the call starts — for a web call that is inside
`POST /v1/webcall`, before its response reaches you, so a tool that must look
the call up by id needs the call [registered first](/web-call#knowing-the-call-id-first).

### create

<ParamField body="name" type="string" required>
  same rules as a variable name. prompts attach the tool as `{{name}}`.
</ParamField>

<ParamField body="description" type="string" required>
  what it does. for a `during_call` tool this is what the model reads to decide
  when to call it — write it for the model.
</ParamField>

<ParamField body="kind" type="string" required>
  `before_call` or `during_call`.
</ParamField>

<ParamField body="config" type="object" required>
  the http call to make:

  * `action` — `get` or `post`; `perform` runs a built-in `performedAction`
    (`end_call`, `transfer_call`) instead of an http call.
  * `method`, `url` (`http(s)://…`), `headers[]`, `params[]`, `body` — any of
    them may contain `{tokens}`.
  * `auth` — `{ "type": "none" | "bearer" | "api_key" | "basic", "token",
    "headerName", "username", "password" }`. **write-only**: the API only ever
    reports `hasSecret`.
  * `timeoutMs` — 100–30000, default 5000. `waitForResponse` — whether a
    `during_call` tool blocks the model until the response arrives.
  * `llmParams[]` — for `during_call` tools, the arguments the model supplies:
    `{ "name", "type": "string" | "number" | "integer" | "boolean",
    "description", "required" }`. each becomes a `{name}` token.
  * `outputs[]` — values the tool exposes: `{ "name", "path" }`. `name` is a
    JSON key looked for anywhere in the response; `path` pins it to one dotted
    location instead.
</ParamField>

tokens are filled, in order of precedence, from the model's arguments
(`llmParams`), then the account's variables, then the context params.

```bash curl theme={null}
curl -X POST https://silk-api.rumik.ai/v1/tools \
  -H "Authorization: Bearer rk_live_•••••••••" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lookup_account",
    "description": "looks the caller'"'"'s account up by phone number",
    "kind": "before_call",
    "config": {
      "action": "get",
      "method": "GET",
      "url": "https://api.example.com/v1/accounts",
      "params": [{ "name": "phone", "value": "{phone_number}" }],
      "auth": { "type": "bearer", "token": "sk-…" },
      "timeoutMs": 3000,
      "outputs": [{ "name": "firstName", "path": "" }, { "name": "plan", "path": "account.plan" }]
    }
  }'
```

```json theme={null}
{
  "id": "019f8e32-4d60-7f71-ac8d-9e0f1a2b3c4d",
  "name": "lookup_account",
  "description": "looks the caller's account up by phone number",
  "kind": "before_call",
  "config": {
    "action": "get",
    "performedAction": "end_call",
    "curl": "",
    "method": "GET",
    "url": "https://api.example.com/v1/accounts",
    "headers": [],
    "params": [{ "name": "phone", "value": "{phone_number}" }],
    "body": "",
    "timeoutMs": 3000,
    "waitForResponse": true,
    "auth": { "type": "bearer", "headerName": "X-API-Key", "username": "", "hasSecret": true },
    "llmParams": [],
    "outputs": [{ "name": "firstName", "path": "" }, { "name": "plan", "path": "account.plan" }]
  },
  "isDefault": false,
  "createdAt": "2026-09-11T10:00:00Z",
  "updatedAt": "2026-09-11T10:00:00Z"
}
```

`PATCH` replaces `config` whole, except that an `auth` block without a
`token` / `password` keeps the stored credential (an empty string clears it).
`DELETE` answers `204`; variables the tool fed fall back to their defaults.
names must not collide with a built-in (`409 agent_tool_name_reserved`); up to
50 tools per account.

## putting them together

<Steps>
  <Step title="a before-call tool fills variables">
    create `lookup_account` as above, then a variable that reads it:
    `{ "name": "first_name", "toolId": "<tool id>", "toolOutput": "firstName" }`.
    at the start of every call the tool runs once with the caller's number and
    `{first_name}` in the prompt and greeting becomes the value it returned —
    or the default if it timed out. to key the lookup on `{call_id}` instead
    (a web call has no caller number), register the call first so you hold the
    id before the tool is asked about it.
  </Step>

  <Step title="a during-call tool is a function">
    create `{ "kind": "during_call", "config": { "action": "post", "url": "https://api.example.com/v1/tickets", "body": "{\"summary\": \"{summary}\"}", "llmParams": [{ "name": "summary", "type": "string", "description": "one line describing the issue", "required": true }] } }`
    and write `{{create_ticket}}` in the prompt where you want the agent to use
    it. the model decides when to call it and supplies `summary`.
  </Step>

  <Step title="built-ins">
    `{{end_call}}` in a prompt lets the agent hang up when the conversation is
    done. it needs no configuration.
  </Step>
</Steps>

<Note>
  headers whose names look like credentials (`authorization`, `cookie`,
  anything containing `key`, `token`, `secret`, …) are stripped from
  `config.headers` — put the credential in `auth` instead, where it is stored
  encrypted.
</Note>

next: [outbound calls](/outbound-calls).
