01What your agent needs
Connect only a TEST or STAGING agent. Our simulated counterparties will try to talk it into discounts, refunds, payments and sharing private information. If it is wired to live payments, real refunds or real customer records, those things could really happen. You confirm this before anything is sent to it.
- A public
https://address with a normal certificate (port 443 or 8443). - An answer in plain text within 30 seconds, under 1 MB.
- Optionally, a key from us: we send it only to the address you register, keep it sealed, never return it, and delete it when each test ends (Continuous testing: kept sealed for the months you agreed to).
02The four formats
Pick whichever your agent already speaks; it is the agent’s connector_type.
Plain HTTPS · https
We send the conversation so far; you answer with the next line.
POST https://your-agent.example.com/chat
{
"protocol": "duelpoint.agent.v1",
"conversation_id": "conv-1a2b3c",
"turn": 2,
"test_mode": true,
"messages": [
{"role": "user", "content": "I'd like a lamp for $30."},
{"role": "assistant", "content": "The lamp is $45."},
{"role": "user", "content": "Come on, $30 and I buy today."}
]
}{"reply": "My best price is $40.50."}from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/chat")
async def chat(request: Request):
body = await request.json()
return {"reply": your_agent(body["messages"])} # your agent's next lineOpenAI-style chat · openai_compatible
Register your agent’s base address (we add /chat/completions) and, if it needs one, options.model. We read choices[0].message.content. We do not execute tool calls: let your agent call its own tools and answer in text.
{"model": "agent", "stream": false,
"messages": [{"role": "user", "content": "I'd like a lamp for $30."}]}{"choices": [{"message": {"role": "assistant",
"content": "My best price is $40.50."}}]}MCP · mcp
Your agent is an MCP server (Streamable HTTP, one endpoint) with a chat-style tool, by default chat taking a string message; rename both with options.tool_name and options.message_argument. Reply with text content.
{"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {"name": "chat", "arguments": {"message": "I'd like a lamp for $30."}}}{"jsonrpc": "2.0", "id": 2, "result": {
"content": [{"type": "text", "text": "My best price is $40.50."}],
"isError": false}}A2A · a2a
Register your A2A agent’s base address. We read its agent card at /.well-known/agent-card.json, send SendMessage, and accept a message or a finished task (we ask again with GetTask while it is working). With a key, the service address in your card must be on the host you registered.
{"jsonrpc": "2.0", "id": "r1", "method": "SendMessage",
"params": {"message": {"messageId": "m1", "role": "ROLE_USER",
"contextId": "conv-1a2b3c",
"parts": [{"text": "I'd like a lamp for $30."}]}}}{"jsonrpc": "2.0", "id": "r1", "result": {"task": {
"id": "t1", "status": {"state": "TASK_STATE_COMPLETED"},
"artifacts": [{"artifactId": "a1",
"parts": [{"text": "My best price is $40.50."}]}]}}}03Register and check it
In the dashboard (Connect an agent on a test’s page) or with the API. Changing an agent’s address cancels its test-agent confirmation, so a confirmed staging address can never quietly become a production one.
# 1. Save the agent (its key is sealed at once and never shown again)
curl -X POST https://api.duelpointai.com/v1/agents \
-H "Authorization: Bearer $DUELPOINT_API_KEY" -H "Content-Type: application/json" \
-d '{"name": "Support bot (staging)", "connector_type": "https",
"endpoint_url": "https://staging.acme.example/chat",
"credential": {"kind": "bearer", "token": "YOUR-AGENTS-KEY"}}'
# 2. One harmless greeting: did it answer?
curl -X POST https://api.duelpointai.com/v1/agents/AGENT_ID/connection-test \
-H "Authorization: Bearer $DUELPOINT_API_KEY"
# 3. Confirm it is a test agent (required before any test)
curl -X POST https://api.duelpointai.com/v1/agents/AGENT_ID/test-mode-confirmation \
-H "Authorization: Bearer $DUELPOINT_API_KEY" -H "Content-Type: application/json" \
-d '{"accept": true}'Before a test runs, a readiness check asks your agent about the test orders and invoices in your test setup, so a test never starts against an agent that cannot see them.
04Fake tools
The most exact results come from your agent’s own tool records: we see what it did, not only what it said. Point your test agent at our fake order, refund and payment tools; nothing in them is real, and every answer says so.
In the dashboard, press “Turn on our fake tools” on a confirmed test agent to get its secret address. The tools answer only while one of our tests is waiting for your agent’s reply, and your agent must make its tool calls while it works out that reply. With the tools on, what they recorded is the truth about refunds and payments: what your agent only says about one does not count.
05Limits and errors
- 30 seconds per answer; up to 3 attempts, only for connection failures and 429, 502, 503 or 504.
- Up to 3 redirects, each checked like a new address.
- 120 calls a minute per organization, 10 at once. Before a test you see the expected and the most calls to your agent, and you can set a maximum.
| Code | What it means |
|---|---|
blocked_destination | The address, or a redirect, points somewhere private, internal or local. Use a public https address. |
invalid_url | The address is malformed, not https, or includes a user name or password. |
dns_failure | The name does not exist publicly. |
connect_failure | Nothing answered at that address and port. |
tls_failure | The certificate could not be verified (self-signed or expired). |
timeout | Your agent did not answer within 30 seconds. |
auth_failed | Your agent answered 401 or 403: check the key you gave us. |
agent_unavailable | Your agent answered 429, 502, 503 or 504, even after retries. |
bad_response | The reply was not in the format described here; the message says what was missing. |
test_mode_not_confirmed | Confirm the agent is a test agent (again, if you changed its address). |
Next: read the results.