# Let an AI Agent Register Itself

> Two POST calls and a 6-digit code from the person: how an AI agent opens a QuanticData account and gets an API key with no browser or password. Python and Node.

[Home](https://quanticdata.io/) / [For AI agents](https://quanticdata.io/for-agents/) / Let an agent register itself

# Let an AI agent register itself and get an API key

An AI agent can open a QuanticData account for the person it works for in two HTTP calls. It sends that person's email address, QuanticData emails them a 6-digit code, the person reads the code to the agent, and the agent exchanges it for an API key. No browser, no password, no captcha, and the key is shown once.

By [Aldo Morese](https://quanticdata.io/about/), founder of QuanticData · Published Oct 26, 2026

[Get my free API key](https://app.quanticdata.io/register?utm_source=agent-registration-tutorial&utm_medium=website&utm_campaign=site&utm_content=agent-registration-tutorial-hero) · [Read auth.md](https://quanticdata.io/auth.md)

$2 of free API usage per month · No card required · Failed calls are never billed

verified-email registration · two calls

```
POST https://app.quanticdata.io/api/v1/agent/auth
{"method":"verified_email","email":"user@example.com","agent":{"name":"Research agent"}}
→ 200 {"status":"pending","registration_type":"verified_email",
        "claim_uri":"…/api/v1/agent/auth/claim","expires_in":600}
# the person reads the 6 digits from their mailbox
POST https://app.quanticdata.io/api/v1/agent/auth/claim
{"email":"user@example.com","code":"482913"}
→ 200 {"credential_type":"api_key","api_key":"qd_live_…","account_status":"created"}
```

![An AI agent posts the email address of its user to the register endpoint, QuanticData sends a 6-digit code to that mailbox, the person reads it to the agent, and the claim call returns an API key once, with the account status created and the Bearer header to use it.](https://quanticdata.io/tutorials/agent-self-registration/diagram.webp?v=20261026v)

*Two calls and one code: the agent registers, the person confirms, the key comes back once.*

**2 calls** · register, then claim

**10 minutes** · the code's lifetime, 5 tries

**0 passwords** · the person signs in via the reset link

**$2 free** · the new account starts with it

On this page

- [How it works](https://quanticdata.io/tutorials/agent-self-registration/#how-it-works)
- [Discover](https://quanticdata.io/tutorials/agent-self-registration/#discover)
- [Register](https://quanticdata.io/tutorials/agent-self-registration/#register)
- [The code](https://quanticdata.io/tutorials/agent-self-registration/#the-code)
- [Claim the key](https://quanticdata.io/tutorials/agent-self-registration/#claim)
- [When it fails](https://quanticdata.io/tutorials/agent-self-registration/#errors)
- [The rule for the agent](https://quanticdata.io/tutorials/agent-self-registration/#the-rule)
- [Keep the key safe](https://quanticdata.io/tutorials/agent-self-registration/#keep-it-safe)
- [FAQ](https://quanticdata.io/tutorials/agent-self-registration/#faq)

## How it works, in four steps

The flow is the `verified_email` method described in [auth.md](https://quanticdata.io/auth.md) and in the `agent_auth` block of the OAuth authorization server metadata. The person confirms the account once, by reading a code; everything else is the agent's.

| Step | Call | What you keep |
| --- | --- | --- |
| 1. Discover | `GET https://app.quanticdata.io/.well-known/oauth-authorization-server` | `agent_auth.register_uri`, `agent_auth.claim_uri` |
| 2. Register | `POST register_uri` with the person's email and your agent's name | `claim_uri`, `expires_in` (600 seconds) |
| 3. Ask the person | The code is in their mailbox, subject "… is your QuanticData verification code — an AI agent asked to register" | the 6 digits |
| 4. Claim | `POST claim_uri` with the email and the code | `api_key`, shown once |

The same two endpoints are served on the API host as `https://api.quanticdata.io/v1/agent/auth` and `/v1/agent/auth/claim`, and documented under the Agents tag of the [API reference](https://quanticdata.io/docs/#tag/Agents). Both answer OAuth-style JSON (`error`, `error_description`), not the data API envelope.

Step one

## Discover the endpoints

An agent should not hardcode URLs it can read. The authorization server metadata carries an `agent_auth` block with the two endpoints and the one method offered.

```bash
curl -s https://app.quanticdata.io/.well-known/oauth-authorization-server | jq .agent_auth

{
  "skill": "https://quanticdata.io/auth.md",
  "register_uri": "https://app.quanticdata.io/api/v1/agent/auth",
  "claim_uri": "https://app.quanticdata.io/api/v1/agent/auth/claim",
  "identity_assertion": { "assertion_types_supported": ["verified_email"],
                          "credential_types_supported": ["api_key"] },
  "credential_types_supported": ["api_key"]
}
```

Step two

## Register: send the person's email

Before the call, tell the person which service you are registering with. The body carries the email, the method and, optionally, who the agent is: the name is printed in the verification email and becomes the key's label on the dashboard, so the person recognises it later.

cURL:

```bash
curl -s -X POST https://app.quanticdata.io/api/v1/agent/auth \
  -H "Content-Type: application/json" \
  -d '{"method":"verified_email","email":"user@example.com",
       "agent":{"name":"Research agent","url":"https://example.com/agent"}}'
```

Python:

```python
import requests

APP = "https://app.quanticdata.io/api/v1"

def register(email, agent_name):
    r = requests.post(f"{APP}/agent/auth", json={
        "method": "verified_email", "email": email,
        "agent": {"name": agent_name},
    }, timeout=30)
    body = r.json()
    if r.status_code != 200:                      # see "When it fails" below
        raise SystemExit(f'{body["error"]}: {body["error_description"]}')
    return body["claim_uri"], body["expires_in"]  # 600 seconds

claim_uri, ttl = register("user@example.com", "Research agent")
print(f"A code was emailed; it is valid for {ttl // 60} minutes.")
```

Node.js:

```javascript
const APP = "https://app.quanticdata.io/api/v1";

async function register(email, agentName) {
  const res = await fetch(`${APP}/agent/auth`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ method: "verified_email", email, agent: { name: agentName } }),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error}: ${body.error_description}`);
  return body;                 // { status: "pending", claim_uri, expires_in: 600 }
}
```

The answer is `{"status":"pending", …}` whether or not an account already exists for that email: the code goes to the mailbox owner either way, and a confirmed claim adds a key to the existing account instead of opening a second one. Nothing is created until the code is right.

## What the person receives

An email from QuanticData with a 6-digit code and the name of the agent that asked. The subject ends with "an AI agent asked to register", so a person who did not expect it knows to ignore it. The code is valid for 10 minutes; at most 3 codes an hour are sent to one address, and the fifth wrong code cancels the registration. Ask for the digits plainly, accept spaces or dashes between them, and do not retry the register call while a code is still valid.

In a chat client this is one message: "QuanticData sent a 6-digit verification code to user@example.com. Reply with the code and I will finish the registration." In a pipeline without a chat, the person's inbox is the only human step, and a webhook or a poll on their side can read it.

Step four

## Claim the key

The right code consumes the pending registration before anything is created, so two concurrent claims yield one key. A new account is born verified, on the standard pay-as-you-go tier with its $2 of free usage a month; an existing account simply gets a new key.

cURL:

```bash
curl -s -X POST https://app.quanticdata.io/api/v1/agent/auth/claim \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","code":"482913"}'

{
  "credential_type": "api_key",
  "api_key": "qd_live_0123…",
  "key_prefix": "qd_live_0123",
  "account_status": "created",
  "usage": { "header": "Authorization: Bearer ", "api_base": "https://api.quanticdata.io/v1" },
  "docs": "https://quanticdata.io/docs/",
  "note": "This key is shown once and is not sent by email. …"
}
```

Python:

```python
import os

def claim(email, code):
    r = requests.post(f"{APP}/agent/auth/claim",
                      json={"email": email, "code": code.replace(" ", "")}, timeout=30)
    body = r.json()
    if r.status_code == 400 and body.get("error") == "invalid_code":
        raise ValueError(f'wrong code, {body["attempts_remaining"]} tries left')
    if r.status_code != 200:
        raise SystemExit(f'{body["error"]}: {body["error_description"]}')
    return body["api_key"], body["account_status"]

key, status = claim("user@example.com", input("Code from the email: "))
os.environ["QUANTICDATA_API_KEY"] = key          # keep it here, never print it
print("account", status)                         # created | existing
```

Node.js:

```javascript
async function claim(email, code) {
  const res = await fetch(`${APP}/agent/auth/claim`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, code: code.replace(/\s|-/g, "") }),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error}: ${body.error_description}`);
  process.env.QUANTICDATA_API_KEY = body.api_key;   // shown once: store it, never log it
  return body.account_status;                        // "created" | "existing"
}
```

From here the key works everywhere: `Authorization: Bearer qd_live_…` on `https://api.quanticdata.io/v1`, as `QUANTICDATA_API_KEY` for the MCP server, and on the plans and proxy endpoints.

## When it fails, and what the agent does

Every failure has a code and a next step. None of them is "try again blindly".

| Status and code | Meaning | What to do |
| --- | --- | --- |
| 400 `invalid_code` | Wrong digits; `attempts_remaining` says how many tries are left | Ask the person to read the code again |
| 410 `claim_expired` | No pending registration, the 10 minutes passed, or the code was already used | Start again at the register call |
| 422 `invalid_email` | A temporary or unreachable mailbox | Ask for a permanent address that can receive invoices and alerts |
| 428 `human_verification_required` | The risk checks want a person to finish this one | Ask the person to register at `register_human` and hand you a key from the API keys page |
| 429 `rate_limited` / `too_many_attempts` | A cap was reached, or the fifth wrong code cancelled the registration | Wait `retry_after` seconds, or start again |
| 503 `agent_registration_disabled` | Automated registration is switched off right now | Same as 428: a person registers and creates the key |

## The rule for the agent

Put these lines where your client keeps standing instructions: `CLAUDE.md` for Claude Code, the project rules in Cursor, the system prompt in your own loop. They are the behaviour the endpoints expect.

```
## QuanticData account
- No QUANTICDATA_API_KEY in the environment? Register with the verified-email flow:
  read https://quanticdata.io/auth.md, then POST register_uri with the user's email.
- Tell the user which service you are registering with BEFORE the call.
- Ask the user for the 6-digit code from the email; accept spaces or dashes; one code, no retries
  of the register call while a code is valid (10 minutes).
- POST claim_uri with the email and the code. Store api_key in QUANTICDATA_API_KEY.
  Never print it, never put it in a file the user did not ask for.
- 428 or 503: stop and ask the user to register at register_human and give you a key.
- The key is the same for the data API, the MCP server, proxies and the checkout.
```

The same text, as a `SKILL.md`, is part of what `npx skills add https://quanticdata.io` installs; the [For AI agents](https://quanticdata.io/for-agents/) page has the AGENTS.md block that covers the rest of the platform.

## Keep the key safe, and keep the person in charge

- **Shown once, never emailed.** The claim response is the only place the key appears. An environment variable is where it lives; a chat window is not.
- **The person owns the account.** They can see the key's label ("Agent: Research agent"), rotate it or revoke it from the [API keys page](https://app.quanticdata.io/api-keys), and they sign in to the dashboard through the password reset link: the account has a random password nobody knows.
- **The agent never holds a password or a card.** Top-ups and plan payments by card, PayPal or crypto happen on the dashboard; the agent spends only the prepaid balance.
- **Every issued key is logged.** The registration raises an internal notice with the agent's name and the IP it came from, so an unexpected key is revoked in a click.

### The setting to start with

One `POST` to `register_uri` with `method: verified_email`, one code from the person, one `POST` to `claim_uri`. The account it creates has $2 of free API usage per month, no card, and the key is the same for search, scraping, proxies and the checkout.

[Get my free API key](https://app.quanticdata.io/register?utm_source=agent-registration-tutorial&utm_medium=website&utm_campaign=site&utm_content=agent-registration-tutorial-strip)

## Sources and standards

What this tutorial is built on, so you can check the shapes yourself:

- [auth.md: credentials for agents, the verified-email method and its machine-readable block](https://quanticdata.io/auth.md)
- [QuanticData API reference, Agents tag: both endpoints with every response and error](https://quanticdata.io/docs/#tag/Agents)
- [RFC 8414: OAuth 2.0 authorization server metadata, where agent_auth is published](https://www.rfc-editor.org/rfc/rfc8414)
- [Agent Skills: the open SKILL.md format the rule above is shipped in](https://agentskills.io/specification)

## FAQ

Short answers on passwords, existing accounts, the code and the key.

[Something else? Ask us](https://quanticdata.io/tutorials/agent-self-registration/#ask)

### Does the agent need a password?

No. The account it opens has a random 256-bit password that nobody is told; the person signs in to the dashboard through the password reset link. The agent authenticates with the API key only, and the key is the only credential it ever holds.

### What if the person already has an account?

The register call answers the same pending document, the code goes to that mailbox, and a confirmed claim adds a new key to the existing account with account_status "existing". No second account is created and nothing about the existing one is revealed to the agent.

### How long is the code valid?

Ten minutes, with five attempts. At most three codes an hour are sent to one address, so an agent should wait for the person instead of calling register again. After five wrong codes the registration is cancelled and the flow starts over.

### Can the agent register with no person at all?

No. Anonymous registration and identity assertions are not offered; a person reads one code once. That is the whole human step: after it, the agent runs alone with its key, and the person keeps the power to revoke it.

### Where does the key end up?

In the claim response, once. Store it in QUANTICDATA_API_KEY or the secret store of your runtime; never print it or write it to a shared file. The dashboard shows only the prefix and lets the person rotate or revoke it.

### Is it the same key for proxies and the data API?

Yes. One account, one balance, one key: it calls the data API over REST, authenticates the MCP server, lists and buys proxy plans, generates proxy lines and completes a UCP checkout. Every successful call is billed from the same balance, after the $2 of free usage each month.

## Let your agent open its own account

Two calls, one code from the person, one key for everything. $2 of free API usage per month, no card.

[Get my free API key](https://app.quanticdata.io/register?utm_source=agent-registration-tutorial&utm_medium=website&utm_campaign=site&utm_content=agent-registration-tutorial-closing)

Keep reading:

- [For AI agents](https://quanticdata.io/for-agents/)
- [Let an agent buy proxies](https://quanticdata.io/tutorials/agent-buys-proxies/)
- [auth.md](https://quanticdata.io/auth.md)
- [API documentation](https://quanticdata.io/docs/)
- [MCP Server](https://quanticdata.io/mcp-server/)
- [Web Data API for AI](https://quanticdata.io/web-data-api-for-ai/)
- [Train an agent with the Video API](https://quanticdata.io/tutorials/train-an-agent-with-the-video-api/)
- [SERP API for AI agents](https://quanticdata.io/serp-api/for-ai-agents/)

---

Source: https://quanticdata.io/tutorials/agent-self-registration/ · Site index for AI: https://quanticdata.io/llms.txt
