# Work References — Full Documentation for AI Agents > Portable, verified employment references anchored to company domains via DNS. ## Table of Contents 1. Overview 2. Key Concepts 3. Protocol Specification 4. API Reference 5. Authentication 6. Onboarding Guide (Agent-Assisted) 7. DNS Setup by Registrar 8. Code Examples 9. Error Handling 10. Rate Limits --- ## 1. Overview Work References is a UK-based platform that lets employers issue tamper-proof employment references. Each reference is: - **Cryptographically signed** using Ed25519 (via TweetNaCl.js) - **Anchored to the employer's domain** via a DNS TXT record - **Portable** — the candidate receives a PDF with embedded verification data - **Instantly verifiable** — anyone can check authenticity without an account The system uses a zero-knowledge architecture: private keys are encrypted client-side with the user's password using PBKDF2 + AES-256-GCM. The server never has access to unencrypted private keys. ## 2. Key Concepts ### Verification Levels | Level | Signature | DNS Record | Meaning | |---------|-----------|------------|------------------------------------------------------| | Gold | Valid | Found | Authentic reference from a verified domain | | Silver | Valid | Not found | Signature valid but DNS record missing or changed | | Invalid | Failed | N/A | Reference payload has been tampered with | ### DNS Record Format ``` Host: REF1._workreferences. Type: TXT Value: v=workreferences1; k=ed25519; p= ``` - `v=workreferences1` — protocol version - `k=ed25519` — key algorithm - `p=...` — base64-encoded Ed25519 public key ### Cryptographic Flow 1. On signup, an Ed25519 key pair is generated in the browser 2. The private key is encrypted with the user's password (PBKDF2 100k iterations + AES-256-GCM) 3. The encrypted private key, salt, and IV are stored server-side 4. The public key is stored in the database AND published as a DNS TXT record 5. To sign a reference, the user enters their password → private key is decrypted in-browser → payload is signed 6. To verify, the verifier checks: (a) Ed25519 signature against the payload and public key, (b) DNS TXT record matches ### Reference Payload Structure ```json { "name": "James Holloway", "role": "Baggage Handler", "dates": "June 2021 – December 2024", "text": "James was a reliable and hardworking member of our team...", "nonce": "uuid-v4", "source": "webapp" } ``` The `nonce` ensures uniqueness. The `source` field indicates how the reference was created (`webapp`, `partner-api`, `tenant-api`). --- ## 3. Protocol Specification ### Record Discovery Given a reference signed by `example.com` with selector `REF1`: 1. Resolve TXT records for `REF1._workreferences.example.com` 2. Find the record matching `v=workreferences1; k=ed25519; p=` 3. Extract the public key from `p=` field 4. Verify the Ed25519 detached signature against the JSON payload ### Signature Verification Algorithm ``` payload_bytes = UTF-8(canonical_json(payload)) signature_bytes = base64_decode(signature) public_key_bytes = base64_decode(public_key) valid = ed25519_verify(payload_bytes, signature_bytes, public_key_bytes) ``` Where `canonical_json` sorts object keys recursively for deterministic output. ### PDF Structure The generated PDF contains: - Visual: Candidate name, role, dates, reference text - QR code linking to verification URL - Metadata fields: `Subject` (JSON payload), `Keywords` (payload-hash, signature, pdf-hash) - The PDF hash is computed as SHA-256 of the PDF bytes and stored in the database --- ## 4. API Reference Base URL: `https://www.workreferences.org` ### GET /api/verify/{id} Verify a reference by UUID. Public, no authentication required. **Response (200):** ```json { "status": "gold", "reference": { "id": "uuid", "candidateName": "James Holloway", "jsonPayload": { "name": "...", "role": "...", "dates": "...", "text": "...", "nonce": "..." }, "signature": "base64...", "revoked": false, "expiresAt": null, "createdAt": "2026-01-15T10:30:00Z" }, "tenant": { "domain": "example.com", "publicKey": "base64...", "keySelector": "REF1" }, "signatureValid": true, "dnsVerified": true } ``` **Status values:** `"gold"`, `"silver"`, `"invalid"` ### POST /api/verify/{id} Same as GET but also accepts a PDF file upload to verify PDF hash. **Request:** `multipart/form-data` with `pdf` field. **Additional response field:** ```json { "pdfHashMatch": true } ``` ### POST /api/tenants/lookup Check if a domain is registered as a Work References tenant. **Request:** ```json { "domain": "example.com" } ``` **Response (200):** ```json { "found": true, "tenant": { "domain": "example.com", "publicKey": "base64...", "keySelector": "REF1", "isDnsVerified": true } } ``` ### GET /api/dns/check?domain=example.com Check if a domain has the Work References DNS TXT record configured. Public, no auth, rate limited (60/min per IP). **Response (200):** ```json { "domain": "example.com", "found": true, "record": "v=workreferences1; k=ed25519; p=abc123...", "selector": "REF1", "host": "REF1._workreferences.example.com", "valid": true } ``` If not found: ```json { "domain": "example.com", "found": false, "record": null, "selector": "REF1", "host": "REF1._workreferences.example.com", "valid": false } ``` ### POST /api/partners/references Create a signed reference. Requires authentication (per-tenant API key or global partner key). **Authentication options:** 1. Per-tenant API key: `x-api-key: rp_...` or `Authorization: Bearer rp_...` 2. Global partner key: `x-partner-key: ` or `Authorization: Bearer ` **Request:** ```json { "tenantDomain": "example.com", "candidateName": "Jane Smith", "role": "Software Engineer", "dates": "Jan 2022 – Dec 2024", "text": "Jane was an exceptional team member...", "signingPassword": "the-tenant-signing-password" } ``` Notes: - `tenantDomain` is required for global key auth, optional for per-tenant key auth - `signingPassword` is always required — it decrypts the private key server-side for signing - Max lengths: candidateName/role 200, dates 100, text 10000 **Response (200):** ```json { "success": true, "reference": { "id": "uuid", "candidateName": "Jane Smith", "pdfHash": "sha256hex..." } } ``` ### GET /api/health Health check endpoint. **Response (200):** ```json { "status": "ok" } ``` --- ## 5. Authentication ### Per-Tenant API Keys Generated from Settings > API Keys in the dashboard. Format: `rp_` followed by 40 hex characters. - Stored as SHA-256 hash only; the full key is shown once at creation - Max 10 keys per tenant - Keys can be revoked (soft-deleted) from Settings - Keys identify the tenant — no need to specify `tenantDomain` in requests **Usage:** ``` x-api-key: rp_a1b2c3d4e5f6... ``` or ``` Authorization: Bearer rp_a1b2c3d4e5f6... ``` ### Global Partner Key Set via `PARTNER_API_KEY` environment variable. Used for platform-level integrations. **Usage:** ``` x-partner-key: ``` or ``` Authorization: Bearer ``` --- ## 6. Onboarding Guide (Agent-Assisted) This is the step-by-step flow an AI agent should follow to help a user set up Work References. ### Step 1: Create Account Direct the user to https://www.workreferences.org/signup The user needs to: 1. Enter their company domain (e.g. `acme.com`) 2. Enter their work email (must be at the same domain) 3. Submit — they'll receive a verification email **Important:** Account creation requires email verification. This step must happen in a browser — it cannot be automated via API. ### Step 2: Complete Email Verification The user clicks the verification link in their email. This takes them to a page where they: 1. Set a signing password (min 12 chars, must include uppercase, lowercase, number, special char) 2. The system generates an Ed25519 key pair and encrypts the private key with this password 3. The system creates the tenant record and returns DNS setup instructions ### Step 3: Detect DNS Provider Once the user has an account, help them set up DNS. First, detect their DNS provider: - Check the domain's nameservers to identify the registrar - Common patterns: - `*.ns.cloudflare.com` → Cloudflare - `dns*.registrar-servers.com` → Namecheap - `ns*.domaincontrol.com` → GoDaddy - `ns-cloud-*.googledomains.com` → Google Domains / Squarespace Use `GET /api/dns/check?domain=` to check if the record is already set up. ### Step 4: Guide DNS Setup Based on the detected registrar, provide step-by-step instructions: **Namecheap:** 1. Sign in to Namecheap → Domain List → Manage → Advanced DNS 2. Add New Record → TXT Record 3. Host: `REF1._workreferences` (don't include domain — Namecheap adds it) 4. Value: `v=workreferences1; k=ed25519; p=` (from signup completion page) 5. TTL: Automatic → Save **GoDaddy:** 1. Sign in → My Products → DNS 2. Add → TXT → Name: `REF1._workreferences` 3. Value: the full record value 4. TTL: 1 Hour → Save **Cloudflare:** 1. Sign in → select domain → DNS → Records → Add Record 2. Type: TXT → Name: `REF1._workreferences` 3. Content: the full record value → Save **Google Domains / Squarespace:** 1. Sign in → select domain → DNS settings 2. Custom Records → Add → Type: TXT 3. Host: `REF1._workreferences` → Data: the full record value → Save **Generic (other registrars):** 1. Go to your DNS management panel 2. Add a TXT record 3. Hostname: `REF1._workreferences` (or `REF1._workreferences.yourdomain.com` if full name required) 4. Value: the full record value from your signup completion page 5. Save ### Step 5: Verify DNS Propagation Poll `GET /api/dns/check?domain=` periodically. - DNS propagation typically takes 5 minutes to 48 hours depending on the registrar - Cloudflare is usually under 5 minutes - Namecheap is typically 30 minutes - GoDaddy is typically 1–4 hours Once `found: true` and `valid: true`, the setup is complete. ### Step 6: Create First Reference or Generate API Key The user can now either: 1. Create a reference from the dashboard (browser) 2. Generate an API key from Settings > API Keys for programmatic access To generate an API key: 1. Go to Settings → API Keys → Create Key 2. Enter a name (e.g. "Production API") 3. Copy the key immediately (shown only once) 4. Use the key with `POST /api/partners/references` --- ## 7. DNS Setup by Registrar ### Namecheap - NS pattern: `*.registrar-servers.com` - Steps: Domain List → Manage → Advanced DNS → Add TXT - Host field: `REF1._workreferences` (no domain suffix) - Propagation: ~30 minutes - Note: Namecheap appends your domain automatically ### GoDaddy - NS pattern: `ns*.domaincontrol.com` - Steps: My Products → DNS → Add → TXT - Name field: `REF1._workreferences` - Propagation: 1–4 hours ### Cloudflare - NS pattern: `*.ns.cloudflare.com` - Steps: DNS → Records → Add Record → TXT - Name field: `REF1._workreferences` - Propagation: ~5 minutes ### Google Domains / Squarespace - NS pattern: `ns-cloud-*.googledomains.com` - Steps: DNS Settings → Custom Records → Add → TXT - Host field: `REF1._workreferences` - Propagation: 15–60 minutes --- ## 8. Code Examples ### Verify a Reference (curl) ```bash curl https://www.workreferences.org/api/verify/REFERENCE_UUID ``` ### Verify a Reference (JavaScript) ```javascript const response = await fetch( 'https://www.workreferences.org/api/verify/REFERENCE_UUID' ); const data = await response.json(); console.log(data.status); // "gold", "silver", or "invalid" ``` ### Verify a Reference (Python) ```python import requests response = requests.get( 'https://www.workreferences.org/api/verify/REFERENCE_UUID' ) data = response.json() print(data['status']) # "gold", "silver", or "invalid" ``` ### Check DNS Record (curl) ```bash curl 'https://www.workreferences.org/api/dns/check?domain=example.com' ``` ### Create a Reference (curl) ```bash curl -X POST https://www.workreferences.org/api/partners/references \ -H 'Content-Type: application/json' \ -H 'x-api-key: rp_your_api_key_here' \ -d '{ "candidateName": "Jane Smith", "role": "Software Engineer", "dates": "Jan 2022 – Dec 2024", "text": "Jane was an exceptional team member...", "signingPassword": "your-signing-password" }' ``` ### Create a Reference (JavaScript) ```javascript const response = await fetch( 'https://www.workreferences.org/api/partners/references', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': 'rp_your_api_key_here', }, body: JSON.stringify({ candidateName: 'Jane Smith', role: 'Software Engineer', dates: 'Jan 2022 – Dec 2024', text: 'Jane was an exceptional team member...', signingPassword: 'your-signing-password', }), } ); const data = await response.json(); console.log(data.reference.id); ``` ### Create a Reference (Python) ```python import requests response = requests.post( 'https://www.workreferences.org/api/partners/references', headers={ 'Content-Type': 'application/json', 'x-api-key': 'rp_your_api_key_here', }, json={ 'candidateName': 'Jane Smith', 'role': 'Software Engineer', 'dates': 'Jan 2022 – Dec 2024', 'text': 'Jane was an exceptional team member...', 'signingPassword': 'your-signing-password', }, ) data = response.json() print(data['reference']['id']) ``` ### Lookup Tenant (curl) ```bash curl -X POST https://www.workreferences.org/api/tenants/lookup \ -H 'Content-Type: application/json' \ -d '{"domain": "example.com"}' ``` --- ## 9. Error Handling All error responses follow this format: ```json { "error": "Human-readable error message" } ``` Common HTTP status codes: - `400` — Bad request (missing/invalid parameters) - `401` — Unauthorized (missing or invalid API key) - `404` — Not found (reference or tenant doesn't exist) - `429` — Rate limited (includes `retryAfter` in seconds) - `500` — Internal server error Rate limit headers on successful responses: - `X-RateLimit-Limit` — Maximum requests per window - `X-RateLimit-Remaining` — Requests remaining - `X-RateLimit-Reset` — Window reset time (ISO 8601) --- ## 10. Rate Limits | Endpoint | Limit | Window | |------------------------------|---------------|----------| | Public verification | 60 requests | 1 minute | | DNS check | 60 requests | 1 minute | | Partner reference creation | 100 requests | 1 minute | | API key creation | 10 requests | 1 hour | | Signup | 5 requests | 1 hour | --- ## Links - Website: https://www.workreferences.org - OpenAPI Spec: https://www.workreferences.org/openapi.json - Short LLM doc: https://www.workreferences.org/llms.txt - Developer Docs: https://www.workreferences.org/implement - API Reference: https://www.workreferences.org/implement/api-reference - Agent Integration: https://www.workreferences.org/implement/agent-integration - Open Standard: https://www.workreferences.org/standard - DNS Setup Guide: https://www.workreferences.org/dns-guide - Demo: https://www.workreferences.org/demo