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

# Tool examples — copy, paste, customize

> Ready-to-paste JSON tool registrations alongside the prompt that generated each one. Use the qlaud assistant to adapt these to your own endpoints, or paste them directly into the dashboard.

This page is a recipe book for **registering tools by pasting a JSON config**.
Every example has two parts:

1. **The prompt** — what to ask the qlaud assistant if you want it to
   generate one of these for your own setup.
2. **The JSON config** — paste this directly into Dashboard → Tools →
   "Custom" tab (paste mode is the default) and click Save.

You can also hit `POST /v1/tools` with the JSON if you'd rather script it.

<Note>
  The qlaud assistant pulls this page into its retrieval context. When
  you ask it "give me a config that…" it pattern-matches against these
  examples, swaps in your specifics, and returns a complete config.
  That's the recommended authoring flow — these are the patterns it
  knows best.
</Note>

## How to read each example

```
### Example name

Prompt:    "Make me a tool that {does X}."
What it does: One-line behavior description.
Replace:   What to swap in before saving (API keys, IDs, URLs).

[JSON config block — copy this whole block]
```

After saving, the tool is immediately available to your model. With
`tools_mode: "tenant"` it auto-attaches to every thread message.

***

## Communication

### Send an email — recipient locked to logged-in user

The most common safe pattern. Use this whenever the AI emails the user
they're chatting with — prevents prompt-injection redirect.

**Prompt:** *"Make me a tool that emails the logged-in user a follow-up."*

**What it does:** Sends transactional email via Resend. The `to` field
is forced to `end_user.metadata.email` regardless of what the model
generates.

**Replace:** `re_REPLACE_ME`, `support@yourdomain.com`. Set
`thread.metadata.email` when creating the thread.

```json theme={null}
{
  "name": "email_user",
  "description": "Send a follow-up email to the logged-in user. The recipient is locked to their session email — model cannot redirect.",
  "provider": "qlaud-builtin/send-email",
  "config": {
    "resend_api_key": "re_REPLACE_ME",
    "from_address": "support@yourdomain.com",
    "lock_to_session": "true"
  }
}
```

### Send an email — to ANY recipient the model picks

Use only when the AI legitimately needs to email a third party (vendor
contact, supplier, outreach prospect). Don't use this for "email my
user" — use the locked variant above.

**Prompt:** *"Make me a tool that sends transactional email via Resend to any address."*

**What it does:** Sends email; recipient picked by the model.

**Replace:** `re_REPLACE_ME`, `support@yourdomain.com`.

```json theme={null}
{
  "name": "send_email",
  "description": "Send a transactional email. Use for outreach, vendor contact, or notifying anyone the user explicitly names.",
  "provider": "qlaud-builtin/send-email",
  "config": {
    "resend_api_key": "re_REPLACE_ME",
    "from_address": "support@yourdomain.com"
  }
}
```

### Send SMS — number locked to logged-in user

**Prompt:** *"Make me a tool that texts the logged-in user via Twilio."*

**What it does:** Sends SMS via Twilio. The `to` field is forced to
`end_user.metadata.phone` (E.164 format).

**Replace:** `AC_REPLACE_ME` (Account SID), `REPLACE_ME` (Auth Token),
`+14155557890` (your verified Twilio number). Set
`thread.metadata.phone` when creating the thread.

```json theme={null}
{
  "name": "sms_user",
  "description": "Send an SMS to the logged-in user. Number locked to their session phone.",
  "provider": "qlaud-builtin/twilio-send-sms",
  "config": {
    "account_sid": "AC_REPLACE_ME",
    "auth_token": "REPLACE_ME",
    "from_number": "+14155557890",
    "lock_to_session": "true"
  }
}
```

### Post to a Slack channel

**Prompt:** *"Make me a tool that posts a notification to our team's Slack channel."*

**What it does:** Posts a plain-text message to a default Slack channel
via `chat.postMessage`. Model can override the channel per call.

**Replace:** `xoxb-REPLACE_ME`, `#support-alerts`.

```json theme={null}
{
  "name": "alert_team_slack",
  "description": "Post an alert to the team's Slack channel. Use for urgent escalations or anomaly notifications.",
  "provider": "qlaud-builtin/slack-post-message",
  "config": {
    "bot_token": "xoxb-REPLACE_ME",
    "default_channel": "#support-alerts"
  }
}
```

***

## Ticketing

### File a Linear issue

**Prompt:** *"Make me a tool that files Linear issues for our engineering team."*

**What it does:** Creates a Linear issue via GraphQL. Title + description

* optional priority come from the model.

**Replace:** `lin_api_REPLACE_ME`, `TEAM-ABC-UUID` (find in Linear team URL).

```json theme={null}
{
  "name": "file_engineering_ticket",
  "description": "File a Linear issue when the user reports a bug or requests a feature.",
  "provider": "qlaud-builtin/linear-create-issue",
  "config": {
    "api_key": "lin_api_REPLACE_ME",
    "team_id": "TEAM-ABC-UUID"
  }
}
```

### File a Zendesk ticket

**Prompt:** *"Make me a tool that opens a Zendesk Support ticket."*

**What it does:** Creates a Zendesk ticket. Optional `requester_email`
links the ticket to a customer; otherwise it's filed under the API user.

**Replace:** `yourcompany` (Zendesk subdomain), `agent@yourcompany.com`,
`REPLACE_ME` (Zendesk API token).

```json theme={null}
{
  "name": "open_zendesk_ticket",
  "description": "Open a Zendesk Support ticket when the user reports an issue that needs human follow-up.",
  "provider": "qlaud-builtin/zendesk-create-ticket",
  "config": {
    "subdomain": "yourcompany",
    "email": "agent@yourcompany.com",
    "api_token": "REPLACE_ME"
  }
}
```

### File a GitHub issue

**Prompt:** *"Make me a tool that opens GitHub issues in our public repo."*

**What it does:** Creates an issue in a specific GitHub repo with
optional labels + assignees from the model.

**Replace:** `ghp_REPLACE_ME`, `youorg`, `yourepo`.

```json theme={null}
{
  "name": "file_github_issue",
  "description": "Open a GitHub issue when the user reports a confirmed bug. Use the labels field to tag triage state.",
  "provider": "qlaud-builtin/github-create-issue",
  "config": {
    "personal_access_token": "ghp_REPLACE_ME",
    "owner": "youorg",
    "repo": "yourepo"
  }
}
```

### Comment on an existing GitHub issue

**Prompt:** *"Make me a tool that comments on existing GitHub issues or PRs."*

**What it does:** Posts a markdown comment on any issue or PR by number.

**Replace:** `ghp_REPLACE_ME`, `youorg`, `yourepo`.

```json theme={null}
{
  "name": "comment_on_issue",
  "description": "Post a follow-up comment on an existing GitHub issue or PR. Use to close the loop on threads we've engaged with before.",
  "provider": "qlaud-builtin/github-add-comment",
  "config": {
    "personal_access_token": "ghp_REPLACE_ME",
    "owner": "youorg",
    "repo": "yourepo"
  }
}
```

***

## Knowledge + retrieval

### Search the public web

**Prompt:** *"Make me a tool the agent can use to search the web."*

**What it does:** Web search via Brave. Returns titled results with
snippets and URLs the model can cite.

**Replace:** `BSA_REPLACE_ME`.

```json theme={null}
{
  "name": "web_search",
  "description": "Search the public web for current information when the question requires post-knowledge-cutoff facts.",
  "provider": "qlaud-builtin/web-search",
  "config": {
    "brave_api_key": "BSA_REPLACE_ME",
    "country": "us",
    "safesearch": "moderate"
  }
}
```

### Search code in our GitHub repo

**Prompt:** *"Make me a tool the agent can use to find code references in our repo."*

**What it does:** Searches code in a pinned repo (or org). Returns up
to 10 file matches with snippets. Scope is fixed at registration so
the model can't widen it to private repos.

**Replace:** `ghp_REPLACE_ME`, `youorg`, `yourepo` (omit `repo` to
search the whole org).

```json theme={null}
{
  "name": "search_codebase",
  "description": "Search code in our repo to find references, examples, or usages before answering.",
  "provider": "qlaud-builtin/github-search-code",
  "config": {
    "personal_access_token": "ghp_REPLACE_ME",
    "owner": "youorg",
    "repo": "yourepo"
  }
}
```

### Read a file from our GitHub repo

**Prompt:** *"Make me a tool the agent can use to read files in our codebase."*

**What it does:** Fetches the contents of a single file at any
ref (branch/tag/sha). 100KB cap.

**Replace:** `ghp_REPLACE_ME`, `youorg`, `yourepo`.

```json theme={null}
{
  "name": "read_codebase_file",
  "description": "Read a single file from our codebase to ground answers in actual code.",
  "provider": "qlaud-builtin/github-get-file",
  "config": {
    "personal_access_token": "ghp_REPLACE_ME",
    "owner": "youorg",
    "repo": "yourepo"
  }
}
```

### Append a page to a Notion database

**Prompt:** *"Make me a tool that logs conversations to a Notion database."*

**What it does:** Adds a new page to a Notion database with title +
optional plain-text body.

**Replace:** `secret_REPLACE_ME`, `DATABASE_UUID`.

```json theme={null}
{
  "name": "log_to_notion",
  "description": "Log this conversation to our Notion knowledge base for future reference.",
  "provider": "qlaud-builtin/notion-append-page",
  "config": {
    "integration_token": "secret_REPLACE_ME",
    "database_id": "DATABASE_UUID"
  }
}
```

***

## Custom (http-call) — for endpoints not in the catalog

### Look up a customer in your internal API

The cleanest "agent reads from my backend" pattern. URL uses
`{{end_user.id}}` directly from the trusted session — no lock needed
because the model can't influence which user is fetched.

**Prompt:** *"Make me a tool the agent can use to look up the logged-in customer in our internal API."*

**What it does:** GET to your internal endpoint, customer scoped to the
session's end\_user\_id, returns whatever JSON your API returns.

**Replace:** `https://api.your-app.com/internal/users/{{end_user.id}}`,
`sk_internal_REPLACE_ME`.

```json theme={null}
{
  "name": "lookup_customer",
  "description": "Look up the logged-in customer's account details (plan, status, last billing date) from our internal API.",
  "provider": "qlaud-builtin/http-call",
  "input_schema": {
    "type": "object",
    "properties": {},
    "required": []
  },
  "config": {
    "method": "GET",
    "url": "https://api.your-app.com/internal/users/{{end_user.id}}",
    "headers": "{\"authorization\": \"Bearer {{secrets.internal_token}}\"}",
    "secrets": "{\"internal_token\": \"sk_internal_REPLACE_ME\"}"
  }
}
```

### High-stakes action with input locked + capped

When the action moves money or changes account state. Lock the
customer id to the session, schema-cap the amount.

**Prompt:** *"Make me a tool that issues partial refunds, but lock it to the logged-in user and cap the amount."*

**What it does:** POSTs a refund to your internal API. `customer_id` is
overridden by `end_user.id` regardless of what the model emits.
`amount_cents` is JSON-Schema-capped at 50000 (\$500).

**Replace:** `https://api.your-app.com/internal/refunds`,
`sk_REPLACE_ME`.

```json theme={null}
{
  "name": "issue_refund",
  "description": "Issue a partial refund to the logged-in customer. Customer id is locked to the session; amount is capped at $500.",
  "provider": "qlaud-builtin/http-call",
  "input_schema": {
    "type": "object",
    "properties": {
      "customer_id": { "type": "string" },
      "amount_cents": { "type": "integer", "minimum": 1, "maximum": 50000 },
      "reason": { "type": "string" }
    },
    "required": ["customer_id", "amount_cents", "reason"]
  },
  "config": {
    "method": "POST",
    "url": "https://api.your-app.com/internal/refunds",
    "headers": "{\"authorization\": \"Bearer {{secrets.api_token}}\", \"content-type\": \"application/json\"}",
    "body_template": "{\"customer_id\":\"{{args.customer_id}}\",\"amount_cents\":{{args.amount_cents}},\"reason\":\"{{args.reason}}\"}",
    "secrets": "{\"api_token\": \"sk_REPLACE_ME\"}",
    "lock_input_fields": "[\"customer_id\"]"
  }
}
```

### Wrap a GraphQL endpoint

**Prompt:** *"Make me a tool that runs a GraphQL search query against our internal docs API."*

**What it does:** Single GraphQL POST. The `query` arg from the model
gets injected into the variables.

**Replace:** `https://api.your-app.com/graphql`, `sk_REPLACE_ME`.

```json theme={null}
{
  "name": "search_internal_docs",
  "description": "Search our internal documentation knowledge base via GraphQL.",
  "provider": "qlaud-builtin/http-call",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": { "type": "string" }
    },
    "required": ["query"]
  },
  "config": {
    "method": "POST",
    "url": "https://api.your-app.com/graphql",
    "headers": "{\"authorization\": \"Bearer {{secrets.api_token}}\", \"content-type\": \"application/json\"}",
    "body_template": "{\"query\":\"query Search($q: String!) { docs(query: $q) { id title snippet } }\",\"variables\":{\"q\":\"{{args.query}}\"}}",
    "secrets": "{\"api_token\": \"sk_REPLACE_ME\"}"
  }
}
```

### Trigger a workflow on your backend (form-encoded body)

**Prompt:** *"Make me a tool that kicks off a long-running workflow on our backend. Body needs to be form-encoded."*

**What it does:** POSTs `user_id={{end_user.id}}&workflow=...` to a
workflow trigger endpoint.

**Replace:** `https://api.your-app.com/workflows/trigger`,
`sk_REPLACE_ME`.

```json theme={null}
{
  "name": "trigger_workflow",
  "description": "Kick off a long-running workflow on the customer's behalf.",
  "provider": "qlaud-builtin/http-call",
  "input_schema": {
    "type": "object",
    "properties": {
      "workflow_name": { "type": "string" }
    },
    "required": ["workflow_name"]
  },
  "config": {
    "method": "POST",
    "url": "https://api.your-app.com/workflows/trigger",
    "headers": "{\"authorization\": \"Bearer {{secrets.api_token}}\", \"content-type\": \"application/x-www-form-urlencoded\"}",
    "body_template": "user_id={{end_user.id}}&workflow={{args.workflow_name}}",
    "secrets": "{\"api_token\": \"sk_REPLACE_ME\"}"
  }
}
```

### Send a Slack DM with metadata-templated channel

A slightly more advanced pattern — the channel comes from
`thread.metadata.csm_channel`, not the model. Useful for
multi-tenant apps where each customer has a dedicated CSM channel.

**Prompt:** *"Make me a tool that posts to the customer's dedicated CSM Slack channel from our metadata."*

**What it does:** Posts to whatever channel the thread metadata
specifies — set per-customer at thread creation.

**Replace:** `xoxb-REPLACE_ME`. When creating the thread, set
`metadata.csm_channel` and `metadata.email`.

```json theme={null}
{
  "name": "alert_csm_channel",
  "description": "Send an alert to this customer's dedicated CSM Slack channel.",
  "provider": "qlaud-builtin/http-call",
  "input_schema": {
    "type": "object",
    "properties": {
      "message": { "type": "string", "description": "What to post." }
    },
    "required": ["message"]
  },
  "config": {
    "method": "POST",
    "url": "https://slack.com/api/chat.postMessage",
    "headers": "{\"authorization\": \"Bearer {{secrets.slack_bot_token}}\", \"content-type\": \"application/json; charset=utf-8\"}",
    "body_template": "{\"channel\": \"{{end_user.metadata.csm_channel}}\", \"text\": \"From {{end_user.metadata.email}}: {{args.message}}\"}",
    "secrets": "{\"slack_bot_token\": \"xoxb-REPLACE_ME\"}"
  }
}
```

***

## Tips for using the qlaud assistant to generate these

When asking the chatbot to generate a config:

1. **Describe the action AND the safety constraint together.** "Email
   the user" is good but "Email the **logged-in** user, lock the
   recipient" is better — it triggers the assistant to pick the
   `lock_to_session` pattern.

2. **Mention if it's an internal API.** "Hit our internal customer
   API" routes to http-call automatically; "send Slack" routes to
   the named scaffold.

3. **Specify the auth shape if it's unusual.** "Bearer token,"
   "Basic auth," "GraphQL with custom headers" — the assistant
   uses these as cues to pick the right preset or http-call config.

4. **Ask for input schema constraints.** "Cap the amount at \$500" or
   "only allow priorities 1-4" — the assistant adds JSON Schema
   minimum/maximum/enum where it makes sense.

5. **Replace secrets last.** The assistant always inserts placeholder
   strings (`re_REPLACE_ME`, `sk_REPLACE_ME`, etc.) — search-and-replace
   them with your real values before pasting into the dashboard.

## Validation rules to keep in mind

If you hand-author or edit a config, the gateway will reject anything
that violates these:

* `name` is required, must be unique per account, can't start with
  `qlaud-builtin/`.
* `provider` must match a slug from `GET /v1/builtins`.
* `input_schema`, when present, must be a valid JSON Schema object.
* For http-call: `url` must start with `http://` or `https://`;
  `headers`, `secrets`, `lock_input_fields` must be valid JSON
  strings (objects for the first two, array of strings for the third).
* All secret-format config fields (API keys, tokens) are AES-GCM
  encrypted at rest and never returned by any read endpoint.

## See also

* [`/api-reference/builtins`](/api-reference/builtins) — full catalog of named scaffolds + lock\_to\_session details
* [`/api-reference/http-call`](/api-reference/http-call) — deep reference for the universal scaffold
* [`/api-reference/tools-modes`](/api-reference/tools-modes) — pick `dynamic` / `tenant` / `explicit` for your request shape
* [`/api-reference/tools`](/api-reference/tools) — webhook tool registration (advanced — for when none of the above fit)
