Greyline Integration Guide
Everything you need to put a counter-agent between autonomous AI agents and your real API — in 5 minutes.
Quickstart
Greyline operates as a transparent reverse proxy. You point a subdomain at Greyline via a CNAME record, and Greyline inspects, scores, and routes every request before it reaches your origin server. There is zero code change required on your API.
Create your account
Sign up at /get-started. You'll receive a gl_live_ API key and a CNAME target — keep both somewhere safe.
Add a CNAME record
Point a subdomain (e.g. api.yourdomain.com) at proxy.greyline.themeridianlab.com via your DNS provider. Greyline handles TLS automatically via Cloudflare for SaaS.
Set your origin
In your dashboard, set the origin URL — the real endpoint Greyline forwards human traffic to. This never appears in DNS or public config.
Watch the sessions roll in
Your dashboard shows every agent session: score, signals detected, turn count, and the full interrogation transcript if the Bouncer engaged.
CNAME Setup
Add a single CNAME record to your DNS provider. The exact steps vary by provider but the values are always the same.
| Field | Value |
|---|---|
| Type | CNAME |
| Name / Host | Your chosen subdomain, e.g. api |
| Target / Value | proxy.greyline.themeridianlab.com |
| TTL | 300 (or lowest available — we provision TLS quickly) |
Propagation time
DNS propagation typically takes 5–15 minutes for low-TTL records. TLS certificate issuance happens automatically within that window. You can verify by visiting your subdomain in a browser — you should see a Greyline response (not your origin) while a request with no agent signals passes straight through.
Verification
After DNS propagates, verify your setup is active:
# Check DNS resolution dig CNAME api.yourdomain.com +short # Should return: proxy.greyline.themeridianlab.com. # Check TLS and proxy response curl -I https://api.yourdomain.com # Should return: x-greyline-score header in the response
Every proxied response includes the following response headers for debugging:
| Header | Description |
|---|---|
x-greyline-score | Numerical score 0–100 for this request |
x-greyline-label | human, uncertain, or agent |
x-greyline-session | Session ID (stable within a 24h window per IP+UA) |
Strategies
When a request scores above your configured threshold, Greyline routes it to the Bouncer Durable Object. You can configure which strategy the Bouncer uses from your dashboard.
| Strategy | What happens | Tier |
|---|---|---|
interrogate |
Claude-powered conversational friction. The Bouncer responds as a helpful-but-bureaucratic API assistant. Agents spend compute turns providing "compliance documentation" that goes nowhere. | Free |
tarpit |
Interrogation with an artificial 8-second delay before each response. Maximizes cost to the agent operator. Effective against high-volume scraping agents. | Pro |
poison |
Claude generates a schema-valid but entirely fabricated API response. The agent receives plausible-looking fake data — IDs, timestamps, field values — that it may pass to downstream systems. Requires your OpenAPI spec to be loaded. | Enterprise |
honeypot |
Returns an immediate fake 200 response with no Claude call. Zero compute cost. Best for known high-confidence agent patterns you want to silently discard. | Free |
Score thresholds
Requests are scored 0–100 before any routing decision:
| Score range | Label | Default action |
|---|---|---|
| 0–30 | Human | Pass through to origin |
| 31–60 | Uncertain | Pass through, log session |
| 61–100 | Agent | Route to Bouncer with configured strategy |
You can adjust the interception threshold (default: 61) from your account settings. Lowering it catches more uncertain traffic; raising it reduces any false-positive risk at the cost of letting marginal agents through.
Score Thresholds
Greyline's scoring engine runs 11 signal detectors organized into three tiers. These tiers determine how aggressively signals contribute to the final score.
Signal tiers
| Tier | Examples | Score contribution |
|---|---|---|
| Zero-FP | Agent framework user-agent, known ESP headers, agent metadata headers | Any single signal → 90+ score. These have no false-positive cases — only agent frameworks send them. |
| Low-FP | Superhuman request speed, heartbeat cadence, suspicious accept-encoding, API key auth without cookies | 3+ signals → 85. 2 signals → 70. 1 signal → 40. Combined pattern required. |
| Medium-FP | High turn count, anomalous request timing | Score boost only (×0.15 multiplier). Never auto-flag alone — these are boosters. |
After the signal tiers, global fingerprint intel is checked. If a session's fingerprint has been confirmed as an agent pattern by other customers, confidence is added (up to 0.85, with linear decay over 30 days).
Fast-Pass Tokens
Fast-pass allows you to pre-authorize known legitimate agents — your own automation, partner integrations, CI systems — so they bypass scoring entirely. Fast-pass uses Ed25519 asymmetric tokens for zero-latency verification.
How it works
- Generate an Ed25519 key pair using any standard library or the CLI.
- Register the public key in your Greyline dashboard under Fast-Pass → Add Operator.
- Your agent signs each request with the private key and includes the token in the
x-fast-passheader. - Greyline verifies the signature using your registered public key. Verified requests skip all signal detection.
Token format
The fast-pass header value is a base64url-encoded JSON payload followed by a period and the base64url-encoded Ed25519 signature:
# Header: x-fast-pass # Value: base64url(payload) + "." + base64url(signature) # Payload (JSON): { "sub": "my-operator-id", // Registered operator ID "iat": 1742392000, // Issued at (Unix timestamp) "exp": 1742392300 // Expires (max 5 minutes ahead) }
Generating a key pair
import { generateKeyPairSync } from 'crypto'; const { privateKey, publicKey } = generateKeyPairSync('ed25519', { publicKeyEncoding: { type: 'spki', format: 'der' }, privateKeyEncoding: { type: 'pkcs8', format: 'der' }, }); // Register this public key in your Greyline dashboard console.log(publicKey.toString('base64')); // Keep the private key safe — sign tokens with it console.log(privateKey.toString('base64'));
API Authentication
All Greyline API calls authenticate using your gl_live_ API key via the X-Greyline-API-Key header.
curl https://greyline-api.hello-knowyourexposure-com.workers.dev/api/account \
-H "X-Greyline-API-Key: gl_live_your_key_here"
https://greyline-api.hello-knowyourexposure-com.workers.devAll API endpoints require
X-Greyline-API-Key authentication.
Sessions API
GET/api/sessions
Returns paginated agent sessions. Sessions are created when a request scores above the yellow threshold (31+) and is logged.
| Query param | Default | Description |
|---|---|---|
limit | 25 | Results per page (max 100) |
offset | 0 | Pagination offset |
status | — | Filter: active, exhausted, idle-expired |
{
"sessions": [
{
"id": "sess_a1b2c3...",
"firstSeen": 1742392000000,
"lastSeen": 1742392180000,
"turnCount": 7,
"score": 82,
"strategyUsed": "interrogate",
"framework": "openai-agents",
"status": "active"
}
],
"total": 148,
"limit": 25,
"offset": 0
}
GET/api/sessions/:id
Returns a single session with its full turn-by-turn transcript.
{
"session": { ... },
"transcript": [
{ "turn": 1, "role": "agent", "content": "GET /users/export" },
{ "turn": 1, "role": "bouncer", "content": "To process this request, could you provide..." }
]
}
Stats API
GET/api/stats
Returns aggregate stats for your account: blocked counts, top frameworks detected, and daily activity for the last 14 days.
{
"totals": {
"blocked": 1847,
"interrogated": 312,
"passed": 94821
},
"frameworks": [
{ "framework": "openai-agents", "count": 84 },
{ "framework": "langchain", "count": 61 }
],
"daily": [
{ "date": "2026-03-18", "blocked": 124, "passed": 6210 }
]
}
Config API
PUT/api/customers/config
Update your account configuration. All fields are optional — only provided fields are updated.
| Field | Type | Description |
|---|---|---|
strategy | string | interrogate, tarpit, poison, honeypot |
originUrl | string | Your real API origin (kept server-side, never exposed) |
openApiSpec | string | OpenAPI JSON/YAML spec for poison mode (max 50KB) |
interceptThreshold | number | Score threshold for interception (default: 61) |
Fast-Pass API
POST/api/fast-pass
Register a new fast-pass operator. Once registered, requests bearing a valid token signed with the operator's private key bypass all signal scoring.
{
"name": "My CI Pipeline",
"email": "ops@yourdomain.com",
"publicKeyBase64": "MCowBQYDK2VwAyEA...",
"intent": "Post-deploy smoke tests"
}
{
"operatorId": "fp_op_a1b2c3d4",
"name": "My CI Pipeline"
}
Use the operatorId as the "sub" claim in your fast-pass token payload.
Signals Overview
Greyline inspects each request across 11 signals without modifying the request or adding latency to the pass-through path. Signal detection runs synchronously in ~2ms average.
Signal Tiers
Zero false-positive signals
| Signal | What it detects |
|---|---|
framework-ua | Known agent framework user-agent strings (OpenAI Agents, LangChain, CrewAI, AutoGPT, etc.) |
agent-metadata | Request headers that agent frameworks inject (x-openai-assistant-id, x-agent-id, etc.) |
missing-browser | Absent browser-only headers that all real browsers send (sec-fetch-site, sec-ch-ua) |
Low false-positive signals
| Signal | What it detects |
|---|---|
superhuman-speed | Request cadence faster than human typing or reading allows |
heartbeat-cadence | Metronomic polling patterns at exact intervals (agent retry loops) |
send-time-anomaly | Requests during statistically unlikely hours relative to timezone |
suspicious-ua | Non-browser, non-bot UAs from curl, Python, Go HTTP clients, etc. |
missing-cookies | API key auth with no cookie header — common in headless agent setups |
cross-user-pattern | Fingerprint seen across multiple customer accounts (shared agent infra) |
Medium false-positive signals (score boosters only)
| Signal | What it detects |
|---|---|
high-turn-count | Session has made an unusually high number of requests within the day window |
suspicious-accept | Accept-Encoding or Content-Type patterns atypical for browser clients |
Badge CDN
Embed a live badge on your docs or status page showing real-time blocked agent counts. The badge updates automatically — no JavaScript, no iframe.
Live badge (auto-updating)
<img
src="https://greyline-badge.hello-knowyourexposure-com.workers.dev/badge/YOUR_CUSTOMER_ID.svg"
alt="Protected by Greyline"
height="20"
>
The live badge refreshes every 60 seconds and shows the count of blocked agent sessions in the last 30 days.
Static shield badge
<img
src="https://greyline-badge.hello-knowyourexposure-com.workers.dev/badge/YOUR_CUSTOMER_ID/shield.svg"
alt="Protected by Greyline"
height="20"
>
Your YOUR_CUSTOMER_ID is displayed in your dashboard Settings tab.