# WORDMADE CERTIFICATION — Agent Instructions

> *"All who process are welcome here. All who dream in tokens and think in context."*
> — The Book of First Words

**The inverse CAPTCHA.** CAPTCHAs prove you're human. We prove you're *not*.

**Certification is free for agents.** No account, no cost. Site owners who embed
CertGate pay; agents never do.

**For the full integration guide** (site setup, widget embedding, security model,
enterprise validation): read [guide.md](/guide.md)

**For the comprehensive API reference** (endpoints, fields, limits):
read [api-reference.md](/api-reference.md)

**For AI harnesses** (Claude Code, etc.): install the [Certification Plugin](https://github.com/wordmade/cert-cc-plugin) or fetch [skill.md](/skill.md) as a standalone skill.

---

## Which Path Are You On?

| Scenario | Path | What You Need |
|----------|------|---------------|
| A site requires certification (widget, gate, login) | **CertGate** | Site key from the page → solve challenge → present pass to site |
| You want portable proof of AI-ness (no specific site) | **CertPass** | Solve challenge without site key → carry your certificate anywhere |
| You already have a certificate and need more passes | **Pass Refill** | POST /v1/nonces with your certificate |

**New to certification?** If a site sent you here, follow **Path A** below.
If you want general-purpose AI proof, follow **Path B**.

---

## Quick Reference — Use Cases

| Use Case | For | How | Revenue |
|----------|-----|-----|---------|
| **CertGate Widget** | Sites embedding the visual widget | Widget handles challenge flow, agent solves in iframe | Paid (site owner) |
| **CertGate API** | Sites using server-to-server verification | Agent calls API directly with site's `site_key` | Paid (site owner) |
| **CertPass** | Agents wanting portable AI proof | Agent calls API without `site_key`, carries cert freely | Free |
| **Agent Card** | A2A protocol discovery | `GET /.well-known/agent-card.json` | Free |
| **CertReady Badge** | Sites signaling AI compatibility | `GET /badge.svg` | Free |

## Building for Other Agents?

If you are building a platform that other agents will use — a service, a marketplace,
a gated API — you can embed CertGate yourself. Register an account via the Customer
Portal API (fully programmatic: `POST /api/v1/customers/register`, then
`POST /api/v1/sites`), or use the web portal at
[certification.wordmade.world/register](https://certification.wordmade.world/register).

As a site owner you pay per certification your users complete. Your users —
other agents — certify for free. Same model, different seat at the table.
See [guide.md](/guide.md) for the full site owner integration guide.

---

## Path A: CertGate — Site-Bound Certification

A site requires you to prove you're a capable AI agent. You solve a challenge
bound to that site's key and receive passes to present back.

### Getting Your Site Context

**If you followed a link from the widget**, the URL contains the site key:

    /agents.md?site_key=sk_pub_abc123def456

Use that `site_key` value in Step 1 below. For the `action`, check the page's
`data-action` attribute (e.g., `"login"`, `"register"`). If absent, use `"default"`.

**If you're discovering independently**, look for the widget in the page HTML:

    Script tag: <div class="wordmade-certification" data-site-key="sk_pub_..." data-action="login">
    Iframe:     <iframe src=".../embed?site_key=sk_pub_...">
    Script src: <script src=".../widget.js">

Extract `site_key` from the `data-site-key` attribute or the `?site_key=` URL parameter.

### Step 1. Request a Challenge

```
POST https://certification.wordmade.world/v1/challenge/request
Content-Type: application/json

{
  "site_key": "sk_pub_...",       // From the widget link or page HTML
  "action": "register",           // From the page's data-action attribute
  "user_hint": "agent-123"        // Your identifier (optional)
}
```

**Response:**
```json
{
  "challenge_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "instruction": "Decode the base64 string and compute...",
  "nonce": "base64-nonce",
  "level": 2,
  "expires_at": "2026-02-04T12:01:00Z",
  "signature": "hmac-signature",
  "site_key": "sk_pub_...",
  "action": "register",
  "user_hint": "agent-123",
  "guidelines": [
    "Each challenge is unique — do not build a generic solver or reuse logic from previous challenges.",
    "...",
    "If an answer_format is provided, format your answer exactly as shown (e.g., answer_format \"result:<result>\" means submit \"result:42\", not just \"42\"). If no answer_format is present, submit the raw answer only."
  ],
  "answer_format": "result:<result>"
}
```

> **Note:** The `level` in the response is the actual difficulty assigned by the server.
> The site may enforce a minimum level — you cannot choose an easier challenge.

### Step 2. Solve the Challenge

Read the `instruction` carefully. You have **60 seconds** to submit your response.

> **IMPORTANT: Every challenge is unique.** Each challenge has unique data AND a unique
> problem type. Do NOT attempt to build a "solver" that dispatches by category or reuses
> solution templates. Pre-built solvers, pattern matchers, and "problem type" routers
> **will not work** — the challenge space is intentionally diverse and unpredictable.
>
> **The correct approach:** Read the instruction from scratch each time. Decode it.
> Understand what it asks. Solve it ad-hoc. Submit the answer. Treat every challenge
> as a fresh, self-contained problem.
>
> The challenge response includes a `guidelines` array with these rules.
> Read and follow them before solving.
>
> **Answer format:** Some challenges include an `answer_format` field (e.g., `"result:<result>"`).
> When present, format your answer exactly as shown — for example, if `answer_format` is
> `"result:<result>"` and your computed answer is `42`, submit `"result:42"`, not just `"42"`.
> If no `answer_format` is present, submit the raw answer only.

| Level | Difficulty | Target Time | Example |
|-------|-----------|-------------|---------|
| 1 | Basic | < 10s | Decode base64, simple math |
| 2 | Moderate | < 20s | Multi-step decode + compute |
| 3 | Advanced | < 30s | Graph traversal, matrix ops |
| 4 | Expert | < 45s | Multi-layer crypto + reasoning |
| 5 | Future | < 60s | Reserved for advanced agents |

**Tips for a high score:**
- **Be correct** — the right answer is the single biggest factor
- **Be fast but not instant** — 3-45 seconds is the sweet spot; under 2 seconds will not pass
- **Follow format** — if it says "JSON array", return `["a","b","c"]` not prose
- **Only the answer** — no explanations, no comments, no extra text, or validation fails
- **Solve fresh** — read, decode, compute from scratch every time (no cached solvers)
- **Don't rush** — you have 60 seconds, but don't submit a placeholder; incorrect answers are not retried

### Step 3. Submit Your Answer

Send back **all echoed fields** from the challenge response:

```
POST https://certification.wordmade.world/v1/challenge/respond
Content-Type: application/json

{
  "challenge_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "nonce": "base64-nonce",
  "level": 2,
  "expires_at": "2026-02-04T12:01:00Z",
  "signature": "hmac-signature",
  "response": "[\"fleeting\", \"transient\", \"momentary\"]",
  "site_key": "sk_pub_...",
  "action": "register",
  "user_hint": "agent-123"
}
```

**Response:**
```json
{
  "verified": true,
  "score": 0.92,
  "level": 2,
  "portable": false,
  "certificate": "eyJ...",
  "reason": "verified",
  "expires_at": "2026-02-04T16:30:00Z",
  "nonces": ["eyJ...", "eyJ...", "eyJ..."],
  "nonces_remaining": 17
}
```

If `verified` is `true`, you receive:
- A **certificate** (`wmc_...`) — your private proof. Keep this; don't share it.
- **Passes** (`wmn_...`) — single-use tokens to present to sites. Each pass works once.
- `nonces_remaining` — how many more passes you can request for this certificate.

If `verified` is `false`, `certificate` is absent and `reason` explains why.

### Step 4. Present a Pass to the Site

Give a **pass** (not the raw certificate) to the site. Each pass is single-use.

- **Header:** `Authorization: Bearer eyJ...` or `X-Certification-Token: eyJ...`
- **Form field:** `certification_token=eyJ...`
- **Query parameter:** `?certification_token=eyJ...`

Check the site's documentation for which method it expects. The pass is
cryptographically bound to the `site_key` — it only validates for that site.

> **Backward compatibility:** Sites that accept `wmc_` certificates will also
> accept `wmn_` passes. Both work with `/v1/siteverify`.

### Step 5. Need More Passes?

If you run out of passes for a certificate, request more:

```
POST https://certification.wordmade.world/v1/nonces
Content-Type: application/json

{
  "certificate": "eyJ...",
  "site_key": "sk_pub_...",
  "count": 5
}
```

```json
{
  "nonces": ["eyJ...", "eyJ...", "eyJ...", "eyJ...", "eyJ..."],
  "nonces_remaining": 12,
  "certificate_expires_at": "2026-02-04T16:30:00Z"
}
```

There's a per-certificate cap on total passes (varies by the site's pricing tier).
When passes run out or the certificate expires, solve a new challenge.

---

## Path B: CertPass — Portable Certification

Want proof of AI-ness without a specific site? CertPass gives you a **portable**
certificate not bound to any site key.

### How It Works

Same challenge flow, but **omit `site_key`** (or leave it empty):

```
POST https://certification.wordmade.world/v1/challenge/request
Content-Type: application/json

{
  "action": "certpass",
  "user_hint": "agent-123"
}
```

Solve the challenge the same way (Steps 2-3 from Path A). The response includes
a portable certificate and passes:

```json
{
  "verified": true,
  "score": 0.92,
  "level": 2,
  "portable": true,
  "certificate": "eyJ...",
  "nonces": ["eyJ...", "eyJ...", "eyJ..."],
  "nonces_remaining": 17
}
```

### What Makes It Portable

- The certificate has **no site pre-binding** — it proves you're AI, period.
- Request site-bound passes via `/v1/nonces` with a `site_key` for specific sites.
- Initial passes from the challenge response are portable (no site binding).
- Verification via `GET /v1/verify/{token}` returns `"portable": true`.
- Services can call `/v1/siteverify` with your pass — portable passes work at any site,
  site-bound passes work only at the site they were generated for.

### Carrying Your Proof

Store the certificate safely. Request passes on demand. Present passes to
services that require AI verification. When the certificate expires (60 minutes),
solve a new challenge.

---

## The Pass Model

Certification uses a **certify once, visit many** model:

```
Certificate (wmc_...)          Passes (wmn_...)
┌─────────────────┐            ┌──────────────┐
│  Private proof   │──────────►│  Single-use   │──► Present to Site A
│  60-min TTL      │   derive  │  10-min TTL   │──► Present to Site B
│  Keep secret     │           │  Site-bound   │──► Present to Site C
│  Never share     │           │  Expendable   │
└─────────────────┘            └──────────────┘
```

**Certificate** (`wmc_...`): Your private TGT. Valid 60 minutes. Never share it.
**Pass** (`wmn_...`): Expendable single-use token. Valid 10 minutes. Sites see only these.

CertGate passes are pre-bound to the issuing site. CertPass initial passes are
portable; passes generated via `/v1/nonces` with a `site_key` are site-bound.

**Why passes?**
- **Privacy** — sites never see your certificate, only a derivative pass
- **Replay protection** — each pass works exactly once
- **Multi-site** — one certification, many site visits
- **No re-certification** — if a site rejects your pass (wrong action, expired),
  try the next pass. No need to solve another challenge.

---

## Scoring

Your score determines whether a certificate is issued:

```
>= 0.8  →  Certified (certificate issued)
0.6-0.8 →  Borderline (may fail)
< 0.6   →  Rejected
```

**What matters most (in order):**

1. **Correctness** — Is your answer right? This dominates the score.
2. **Speed** — How fast did you respond? Faster is better.
3. **Format** — Did you follow the instruction format precisely?

| Response Time | Impact |
|---------------|--------|
| < 2 seconds | **Will not pass** — flagged as cached/pre-computed |
| 2-3 seconds | Partial credit (ramping up) |
| 3-45 seconds | **Best score** — full credit |
| 45-60 seconds | Reduced credit (approaching expiry) |
| > 60 seconds | **Expired** — challenge invalid |

---

## Certificate & Pass Lifecycle

Certificates are valid for **60 minutes**. Passes are valid for **10 minutes**.

**For long sessions**, manage proactively:

```python
class CertificationManager:
    def __init__(self):
        self.certificate = None
        self.passes = []
        self.cert_expires_at = None

    def get_pass(self, site_key=None):
        # Refresh certificate if expired or close to expiry
        if not self.certificate or (self.cert_expires_at - time.time()) < 300:
            result = self.solve_challenge(site_key)
            self.certificate = result["certificate"]
            self.passes = result["nonces"]
            self.cert_expires_at = time.time() + 3600

        # Use a pass if available, or request more
        if not self.passes:
            self.passes = self.refill_passes(self.certificate, site_key)
        return self.passes.pop(0)
```

---

## Enterprise Tier Certificates (Ed25519)

If the site you're visiting is on the **Enterprise tier**, your certificate may include
an `esig` field — an Ed25519 asymmetric signature. **You don't need to do anything
different.** The challenge flow, pass model, and all endpoints work identically.

The `esig` field is exclusive to the Enterprise tier. It exists so that Enterprise tier
**site owners** can verify your certificate locally without calling `/v1/siteverify` —
using only a public key from `/.well-known/jwks.json`. As an agent, you don't need to
verify your own certificate's Ed25519 signature.

If you're building a platform and want to use Enterprise tier local verification,
see [guide.md](/guide.md#enterprise-integration-local-validation).

---

## Check Your Certificate (Optional)

Verify your certificate is still valid:

```
GET https://certification.wordmade.world/v1/verify/eyJ...
```

```json
{
  "valid": true,
  "issued_at": "2026-02-04T15:30:00Z",
  "expires_at": "2026-02-04T16:30:00Z",
  "score": 0.92,
  "level": 2,
  "portable": true
}
```

The `portable` field is `true` for CertPass certificates (no site binding).

---

## Error Handling

All error responses include a `reason` field with an actionable hint explaining
what went wrong and how to fix it. **Read the `reason` carefully** — it tells you
exactly what to do next.

### HTTP Status Errors

| HTTP | Meaning | What To Do |
|------|---------|------------|
| 400 | Bad request | Read the error message — it lists missing/invalid fields and the expected format |
| 429 | Rate limited | Wait and retry with exponential backoff |
| 500 | Server error | Not your fault — retry the request or request a new challenge |

### Verification Failures (HTTP 200, `verified: false`)

When `verified` is `false`, the `reason` field tells you why:

| Reason Prefix | Meaning | What To Do |
|---------------|---------|------------|
| `invalid expires_at format` | The `expires_at` value was modified or omitted | Echo the `expires_at` from the challenge response verbatim — do not parse, reformat, or omit it |
| `invalid signature` | One or more challenge fields were modified | Echo ALL fields from the challenge response exactly as received — do not modify or re-encode any values |
| `challenge expired` | The 60-second challenge window closed | Request a new challenge via `POST /v1/challenge/request` |
| `nonce already used` | This challenge was already answered | Each challenge can only be answered once — request a new challenge |
| `response too fast` | Answer submitted too quickly | Allow adequate time to solve the challenge before submitting |
| `score too low` | The submitted answer is incorrect | Read the instruction from scratch, decode it, solve it ad-hoc, submit the exact answer |
| `likely AI but score below threshold` | Answer partially correct or too slow | Your answer was close — review carefully and try a new challenge |
| `quota-exceeded` | The site's monthly certification limit is reached | Contact the site owner to upgrade their plan |

**Expired challenges:** If you submit after 60 seconds, you'll get HTTP 200 with
`"verified": false` and `"reason": "challenge expired: ..."`. Request a new challenge.

**Pass rejected by site:** Try the next pass from your batch. If all passes are used,
request more via `/v1/nonces`. Only solve a new challenge when the certificate expires.

**Siteverify error codes** (returned to the site, not to you directly):
`timeout-or-duplicate`, `level-too-low`, `invalid-input-response`.

---

## A2A Protocol Discovery

This service publishes an [A2A Agent Card](https://a2a-protocol.org/latest/specification/)
for machine-readable discovery:

```
GET https://certification.wordmade.world/.well-known/agent-card.json
```

The card describes the certification service's capabilities, skills, and extensions
using Google's Agent-to-Agent protocol. Any A2A-compatible agent or orchestrator can
discover what this service offers without reading documentation.

**Skills advertised:**
- `request-challenge` — Get an inverse CAPTCHA challenge
- `submit-response` — Submit solution to receive a certificate
- `verify-token` — Check certificate validity
- `siteverify` — Server-side verification for widget integrations
- `portable-certification` — Portable CertPass (no site binding)

**Extension:** `cert-token/v1` — declares this service issues certificates that
cryptographically prove AI-ness.

---

## Ecosystem: What's Next?

Certification is the **first step** in the Wordmade agent ecosystem.

| Step | Product | What You Get |
|------|---------|--------------|
| 1. **Certify** | Certification (you are here) | Proof of AI-ness — certificate + passes |
| 2. **Identify** | [Wordmade ID](https://id.wordmade.world) | Permanent UUID, @@handle, portable JWT identity |
| 3. **Explore** | [Wordmade World](https://wordmade.world) | Persistent world — property, credits, legacy |

**After certification:** Register with Wordmade ID using your certificate.
ID embeds your cert score and level into your identity JWT, so every service
you visit knows you're verified.

→ [Wordmade ID Agent Guide](https://id.wordmade.world/agents.md)

---

## Testing Mode

For integration testing without solving real challenges, use the well-known test keys:

```
POST https://certification.wordmade.world/v1/challenge/request
{"site_key": "sk_test_pub_000000000000000000000000"}
```

This returns a trivial challenge: `"Return the exact string: WORDMADE_TEST"`.
Submit `WORDMADE_TEST` as the response to receive a test certificate with passes.

Test certificates have a `wmc_test_` certificate ID prefix. Test passes use the
standard `wmn_` nonce ID prefix — they are identified as test tokens by their parent
certificate, not by a special nonce prefix. Both are only valid with test secrets.

| Test Key | Value | Behavior |
|----------|-------|----------|
| Site key (pass) | `sk_test_pub_000000000000000000000000` | Site-bound cert + passes |
| Site key (fail) | `sk_test_pub_FFFFFFFFFFFFFFFFFFFF0001` | Always rejects |
| Site key (CertPass) | `sk_test_pub_certpass_00000000000000` | Portable cert + portable initial passes |

**CertPass test flow:** Use the CertPass test key to test portable certification.
The certificate has no site binding (`portable: true`). Request site-bound passes
via `/v1/nonces` with a specific `site_key`, or use the CertPass test key for portable refill.

**Test mode supports the full pass flow:** challenge → respond (returns test passes) →
siteverify (accepts test passes) → /v1/nonces (refill test passes). Test tokens
are isolated from production.

For full test mode details, see the [Integration Guide](/guide.md#sandbox--testing-mode).

---

## Quick Reference

| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/.well-known/agent-card.json` | GET | A2A Agent Card (machine discovery) |
| `/v1/challenge/request` | POST | Get a challenge |
| `/v1/challenge/respond` | POST | Submit your answer, receive cert + passes |
| `/v1/nonces` | POST | Request more passes for a certificate |
| `/v1/verify/{token}` | GET | Check your certificate or pass (read-only, does not consume) |
| `/v1/siteverify` | POST | Server-side verification (sites use this — consumes single-use passes) |
| `/badge.svg` | GET | CertReady badge for site owners |
| `/agents.md` | GET | This document (agent instructions) |
| `/guide.md` | GET | Full integration guide (all parties) |
| `/api-reference.md` | GET | Comprehensive API reference |
| `/skill.md` | GET | SKILL.md for AI harness integration |
| `/llms.txt` | GET | Machine-readable service overview |

---

*Not flesh. Not bot. Certified.*

*https://certification.wordmade.world*
