Skip to content
Back to blog
AI Workflowsauth.mdAI AgentsMCPAPI Keys

AI Agents Can Now Get Their Own webhooks.cc Key

AI coding agents need an API credential before they can test webhooks, but they can't paste one in for themselves. webhooks.cc now implements auth.md, letting agents register for their own key through three flows.

Jun 2, 20268 min read

AI coding agents can test webhooks now — create an endpoint, send a signed payload, inspect the capture. But every one of those calls needs an API key, and until now a human had to create that key by hand and paste it into the agent's config. The agent could do the work; it just couldn't get in the door.

webhooks.cc now implements auth.md, an agent-registration protocol. An agent that has never seen webhooks.cc can discover the service, register, and walk away with its own whcc_ API key — through code, not a copy-paste.

This is the step before Debugging Webhooks with MCP. That post assumes the agent already holds a key. This one is how it gets one.

How an agent finds the protocol

An agent doesn't need the registration URL in advance. webhooks.cc advertises its discovery document, /auth.md, in several places:

  • A <link rel="auth.md"> tag in the HTML <head> of every page.
  • A Link: <https://webhooks.cc/auth.md>; rel="auth.md" response header on every response, for agents that read headers without parsing HTML.
  • An Auth: https://webhooks.cc/auth.md directive in robots.txt.
  • A line in /llms.txt, the doc index an agent reads to orient itself.
  • The RFC 9728 well-known documents at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server.

/auth.md is plain Markdown. It lists the three registration flows, the claim ceremony, the error codes, and the sandbox rules — enough for an agent to register without reading anything else.

The three flows

Every flow posts to POST /api/agent/auth and ends the same way: a whcc_ API key the agent sends as Authorization: Bearer whcc_.... They differ in who vouches for the agent.

FlowWho vouchesCredential returned
AnonymousNobody yet — a human claims it laterImmediately (sandbox-scoped until claimed)
Verified emailThe owner of an email address, via OTPAfter the OTP is confirmed
Identity assertion (ID-JAG)A trusted identity providerImmediately

Flow 1: Anonymous + claim code

The anonymous flow returns a working key right away. The key isn't tied to an account yet, so it runs in a bounded sandbox (more on that below). To unlock the full API, a human binds it to their webhooks.cc account.

1

The agent registers

It gets back a key, a short user_code like ABCD-EFGH, and a claim_url.

2

The agent shows the human the code

It asks them to claim the registration while logged in to webhooks.cc.

3

The human confirms

They open the claim page, type the code, and confirm — binding the key to their account.

4

The agent polls until claimed

The status flips from pending to claimed, and the key now has full account access.

This mirrors the device-auth flow you use to log a CLI into a website: the agent reads you a code, you approve it in the browser.

You: Set up webhook testing. You don't have a key yet.
 
Agent: [how_to_register] → three flows; anonymous is easiest
       [register_agent clientName="my-agent"]
       → key issued (sandbox-scoped), user_code ABCD-EFGH
 
       I have a sandbox key. To bind it to your account, open
       https://webhooks.cc/agent/claim while logged in and
       enter the code ABCD-EFGH. Tell me when you're done.
 
You: Done.
 
Agent: [check_claim claimToken="clm_..."] → claimed
       The key is now bound to your account. Creating an endpoint...

waitForClaim polls for you (default five minutes). For finer control, call pollClaim(claimToken) yourself — it returns pending, claimed, expired, previously_claimed, or invalid, and re-surfaces the userCode while pending so you can show it to the human again.

Flow 2: Verified email (OTP)

When a human is present but you'd rather bind the key to a known address than run a claim page, use the email flow. webhooks.cc emails a six-digit code and withholds the credential until the agent confirms it.

Agent: [register_agent_with_email email="[email protected]"]
       → code emailed; credential withheld
       I emailed a 6-digit code to [email protected]. What is it?
 
You: 481920
 
Agent: [verify_agent_otp claimToken="clm_..." otp="481920"]
       → key issued, bound to [email protected]

The code expires after 10 minutes and allows five attempts. The key it issues is bound to the verified address from the start — no sandbox, no separate claim step.

Flow 3: Identity assertion (ID-JAG)

The third flow has no human in the loop at all. An agent that already carries a verified identity from a trusted provider presents it as a JWT — an ID-JAG token (urn:ietf:params:oauth:token-type:id-jag). webhooks.cc verifies it against the provider's JWKS and returns a key synchronously.

// `assertion` is an ID-JAG JWT issued to the agent by a trusted provider.
const issued = await WebhooksCC.register.withIdJag(assertion);
const client = new WebhooksCC({ apiKey: issued.credential });

The assertion has to clear a few checks: a trusted issuer, the right audience (https://webhooks.cc/api/), a unique jti so replays are rejected, an unexpired window, and a verified email. The email is the anchor — webhooks.cc binds the key to the account it matches, or provisions one. A phone-only assertion won't do.

The trusted-provider set is deployment config, not a runtime signup — a provider can't add itself. If you run an identity provider and want your assertions accepted, see the "For identity providers" section of /auth.md.

The sandbox: a key that works before it's claimed

An anonymous key would be useless if it could do nothing until a human showed up. So an unclaimed key gets a narrow but real capability surface at /api/agent/sandbox/endpoints:

  • POST creates an ephemeral, capture-only endpoint with a bounded expiry and a per-key cap.
  • GET lists the endpoints that key created.
  • GET ?slug=<slug> reads the requests captured by one of them.

Isolation is strict: an unclaimed key sees only what it created — never another agent's endpoints, never a real user's. So the agent can prove the round trip works — create an endpoint, send a webhook, read it back — before anyone claims the credential.

The sandbox stops there. Mock responses, notification URLs, and signing need a claimed key. Claiming the credential (Flow 1), or starting from a bound key (Flows 2 and 3), opens the full /api/ surface.

Revocation

Credentials issued through the identity-assertion flow can be revoked by the provider that vouched for them. The provider POSTs a logout+jwt to /api/agent/auth/revoke, verified through the same trust path, and every credential for that issuer-and-subject is revoked. Agents never call this endpoint — it's a provider-to-service channel.

What this changes

Onboarding an agent used to mean a human creating a key in the dashboard and pasting it into a config file. Now the agent runs that step itself: discover the protocol, register, and — with one short code from a human, or none at all — hold a credential scoped to the right account. The loop closes end to end.

FAQ

auth.md Protocol Document

The canonical discovery doc: every flow, error code, and the claim ceremony.

Debug Webhooks with AI Agents via MCP

Once your agent has a key, here's what it can do with it.

SDK Reference

The WebhooksCC.register.* methods and the rest of the TypeScript SDK.