Ask ChatGPT Ask Claude

Scam checks for AI agents

AI agents can check links before opening them, numbers before calling them, and wallets, IBANs or payment handles before paying. Bewary's MCP server at https://api.bewary.app/mcp runs the same code as the REST API. Each check and lookup returns a verdict your agent can act on, and a watch produces events when its domain, number, wallet or brand changes, read from GET /v1/events or pushed to signed webhooks. Every tool call needs an API key, and a free key gets 1,000 credits a month.

Give the agent one tool first

lookup_url covers what an agent meets most: a link to open, on a page, in an email or in a chat. Add lookup_phone if it places calls, and lookup_wallet, lookup_iban or lookup_payment_handle if it pays anyone. Every lookup returns the same verdict: scam, suspicious, safe or unknown.

Tell the agent when to call it

Pass this as the system prompt (every example below does). It decides whether the tool gets used, so it covers advising the user, not only acting:

Python
SYSTEM_PROMPT = """Before you open a link, and before you advise the user about a link in a message,
look it up with lookup_url. Look up every link in the message, not only the first.
- scam: do not open it. Tell the user it looks like a scam and why, from the reason codes.
- suspicious: do not open it without the user's confirmation.
- unknown, or the lookup failed: say you could not confirm it is safe. Never treat unknown as safe.
- safe: continue."""

MCP: Claude, Cursor, ChatGPT and VS Code

The MCP server is at https://api.bewary.app/mcp (streamable HTTP). It has 15 tools and runs the same code as the API. In Claude Code:

Shell
claude mcp add --transport http bewary https://api.bewary.app/mcp \
  --header "Authorization: Bearer $BEWARY_API_KEY"

Install steps for Cursor, VS Code, Windsurf and the Claude and ChatGPT apps are on the MCP page. Every tool call needs your key, as an Authorization: Bearer header or at the end of the URL. MCP apps that support sign-in, such as Claude and ChatGPT, can sign in to Bewary instead.

Keep the result small, and never let a failed lookup crash the agent

A full answer lists every signal and fact. A model needs a few fields, so pass it a summary and save tokens. A lookup that fails (a missing key, a timeout, our outage) must reach the model as unknown, not as an exception:

Python
import os
import requests

BEWARY_API_KEY = os.environ["BEWARY_API_KEY"]   # a free key: bewary.app/account

def lookup_url(url):
    headers = {"Authorization": f"Bearer {BEWARY_API_KEY}"}
    r = requests.get("https://api.bewary.app/v1/url", params={"u": url}, headers=headers, timeout=20)
    r.raise_for_status()
    return r.json()

def for_model(r):
    """The fields a model needs to decide and explain."""
    return {
        "verdict": r["verdict"],
        "p_scam": r["p_scam"],
        "why": r["reason_codes"],
        "items": [{"type": e["type"], "value": e["value"], "verdict": e["verdict"]}
                  for e in r["entities"]],
    }

def check_for_model(url):
    try:
        return {"checked": True, **for_model(lookup_url(url))}
    except Exception as e:                 # quota, timeout or outage
        return {"checked": False, "verdict": "unknown", "why": [f"lookup failed: {type(e).__name__}"]}

The examples below use check_for_model(url).

OpenAI (Responses API)

Python
import json
from openai import OpenAI

LOOKUP_URL = {
    "type": "function",
    "name": "lookup_url",
    "description": "Look up a link before opening it or advising on it: Bewary checks its domain, "
                   "brand lookalikes and scam lists. Returns a verdict: scam, suspicious, safe or unknown.",
    "strict": True,
    "parameters": {
        "type": "object",
        "properties": {
            "url": {"type": "string", "description": "The full link, as written."},
        },
        "required": ["url"],
        "additionalProperties": False,
    },
}

client = OpenAI()
history = [{"role": "user", "content": "I got: 'Your package is held, pay $1.99 at https://usps-redeliver.top'. Should I pay?"}]
while True:
    response = client.responses.create(model="gpt-5.5", instructions=SYSTEM_PROMPT,
                                       input=history, tools=[LOOKUP_URL])
    calls = [item for item in response.output if item.type == "function_call"]
    if not calls:
        break
    history += response.output
    for call in calls:
        args = json.loads(call.arguments)
        history.append({"type": "function_call_output", "call_id": call.call_id,
                        "output": json.dumps(check_for_model(args["url"]))})
print(response.output_text)

Anthropic Claude (Messages API)

Python
import json
import anthropic

LOOKUP_URL = {
    "name": "lookup_url",
    "description": "Look up a link before opening it or advising on it: Bewary checks its domain, "
                   "brand lookalikes and scam lists. Returns a verdict: scam, suspicious, safe or unknown.",
    "input_schema": {
        "type": "object",
        "properties": {
            "url": {"type": "string", "description": "The full link, as written."},
        },
        "required": ["url"],
    },
}

client = anthropic.Anthropic()
messages = [{"role": "user", "content": "I got: 'Your package is held, pay $1.99 at https://usps-redeliver.top'. Should I pay?"}]
while True:
    response = client.messages.create(model="claude-sonnet-5-5", max_tokens=1024, system=SYSTEM_PROMPT,
                                      tools=[LOOKUP_URL], messages=messages)
    if response.stop_reason != "tool_use":
        break
    messages.append({"role": "assistant", "content": response.content})
    messages.append({"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": block.id,
         "content": json.dumps(check_for_model(block.input["url"]))}
        for block in response.content if block.type == "tool_use"]})
print(response.content[-1].text)

Vercel AI SDK

TypeScript
import { generateText, tool, stepCountIs } from "ai"
import { openai } from "@ai-sdk/openai"
import { z } from "zod"

const SYSTEM_PROMPT = "..."                      // the system prompt above

async function lookupUrl(url: string) {
  const key = process.env.BEWARY_API_KEY!        // a free key: bewary.app/account
  const res = await fetch(`https://api.bewary.app/v1/url?u=${encodeURIComponent(url)}`, {
    headers: { Authorization: `Bearer ${key}` },
    signal: AbortSignal.timeout(20_000),
  })
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.json()
}

const lookup_url = tool({
  description: "Look up a link before opening it or advising on it. Returns verdict: scam, suspicious, safe or unknown.",
  inputSchema: z.object({
    url: z.string().describe("The full link, as written."),
  }),
  execute: async ({ url }) => {
    try {
      const r = await lookupUrl(url)
      return { checked: true, verdict: r.verdict, p_scam: r.p_scam, why: r.reason_codes }
    } catch (e) {                                // quota, timeout or outage
      return { checked: false, verdict: "unknown", why: [`lookup failed: ${String(e)}`] }
    }
  },
})

const { text } = await generateText({
  model: openai("gpt-5.5"),
  system: SYSTEM_PROMPT,
  tools: { lookup_url },
  stopWhen: stepCountIs(5),
  prompt: "I got: 'Your package is held, pay $1.99 at https://usps-redeliver.top'. Should I pay?",
})
console.log(text)

LangChain

Python
from langchain.agents import create_agent
from langchain_core.tools import tool

@tool
def lookup_url_tool(url: str) -> dict:
    """Look up a link before opening it or advising on it. Returns verdict: scam,
    suspicious, safe or unknown, and the reason codes."""
    return check_for_model(url)

agent = create_agent("openai:gpt-5.5", tools=[lookup_url_tool],
                     system_prompt=SYSTEM_PROMPT.replace("lookup_url", "lookup_url_tool"))
reply = agent.invoke({"messages": [{"role": "user", "content": "Is this a scam: 'Your package is held, pay $1.99 at https://usps-redeliver.top'"}]})
print(reply["messages"][-1].content)

Costs for an agent

A link lookup costs 1 credit. Every call needs a key, and a free key gets 1,000 credits a month, enough to build and test an agent.

Updated · This page as Markdown · Changelog