Skip to main content

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.

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.
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.

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.
{
  "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.
{
  "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.
{
  "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.
{
  "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).
{
  "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).
{
  "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.
{
  "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.
{
  "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.
{
  "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).
{
  "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.
{
  "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.
{
  "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.
{
  "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.
{
  "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.
{
  "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.
{
  "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.
{
  "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