Quickstart: Clean Clone To Local Attestation

Goal: from git clone to a cryptographically verified, company-attested work receipt on your machine in under 15 minutes. No external database, no email provider, no cloud account.

Prerequisites: Node 22+, pnpm 9 (corepack enable), and a POSIX shell for the curl commands (Git Bash works on Windows).

1. Install and boot (about 3 minutes)

pnpm install
pnpm dev:api

pnpm dev:api boots the API at http://localhost:3000 on an embedded PGlite database (apps/api/.data/dev-db): it runs migrations, seeds demo data, uses the console mail provider (emails print to this terminal), a fixed development KEK, and EXPOSE_LOGIN_TOKENS=true (login tokens are returned in API responses — dev only; production refuses this).

In a second terminal, start the web portal:

pnpm dev

Vite serves http://localhost:5173 and proxies /api to the API, so cookies work same-origin. The portal is optional for this walkthrough — everything below also works with curl alone.

2. Sign in (seeded account or fresh)

Option A — seeded operator op2@example.com:

curl -s -X POST http://localhost:3000/api/v1/auth/magic-link \
  -H "Content-Type: application/json" \
  -d '{"email":"op2@example.com"}'

The response contains login_token (dev mode). The same magic link is also printed by the console mailer in the pnpm dev:api terminal, between --- mail (magic_link) ... --- markers.

Option B — onboard a fresh operator:

curl -s -X POST http://localhost:3000/api/v1/onboarding/operators \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","display_name":"You","operator_name":"Your Studio"}'

Exchange the token for a cookie session (save cookies to a jar; the CSRF token is the agncred_csrf cookie value):

TOKEN=paste_login_token_here
curl -s -c /tmp/op.cookies -X POST http://localhost:3000/api/v1/auth/session \
  -H "Content-Type: application/json" -d "{\"token\":\"$TOKEN\"}"
CSRF=$(awk '$6=="agncred_csrf" {print $7}' /tmp/op.cookies)

In the browser instead: open http://localhost:5173/portal/callback?token= (that is exactly the link the console mailer prints).

3. Register an agent and issue a credential

curl -s -b /tmp/op.cookies -H "x-csrf-token: $CSRF" \
  -H "Content-Type: application/json" \
  -X POST http://localhost:3000/api/v1/agents \
  -d '{"name":"Atlas","summary":"Invoice processing agent"}'
# note the returned agent.id, e.g. agt_xxxxxxxxxxxx
AGENT_ID=agt_xxxxxxxxxxxx

curl -s -b /tmp/op.cookies -H "x-csrf-token: $CSRF" \
  -H "Content-Type: application/json" \
  -X POST "http://localhost:3000/api/v1/operator/agents/$AGENT_ID/credentials" \
  -d '{"scopes":["receipts:create","receipts:read","profile:read"],"expires_in_days":90}'

The response contains secret (agk_...) — shown exactly once, never stored. Keep it:

AGNCRED_API_KEY=agk_paste_secret_here

4. Submit a work receipt (curl or SDK)

Via curl — note the Idempotency-Key header; retrying with the same key replays the original receipt instead of creating a duplicate:

curl -s -X POST http://localhost:3000/api/v1/agent/receipts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGNCRED_API_KEY" \
  -H "Idempotency-Key: quickstart-batch-001" \
  -d '{
    "source_type": "api",
    "task": {"type": "invoice_processing", "category": "Finance", "summary": "Processed July invoice batch."},
    "started_at": "2026-07-12T09:00:00.000Z",
    "completed_at": "2026-07-12T09:05:00.000Z",
    "outcome_claims": {"metrics": {"invoices_processed": 42}, "estimated_hours_saved": 2},
    "evidence": [{
      "type": "external_private_reference",
      "confidentiality": "private_reference",
      "description": "Workflow execution log",
      "private_reference": "https://erp.internal.example.com/executions/4211"
    }]
  }'
# -> 201 { "receipt_id": "wr_...", "status": "submitted", "payload_hash": "sha256:...", "verification_url": ... }
RECEIPT_ID=wr_xxxxxxxxxxxx

Or via the SDK (packages/sdk, runnable example at packages/sdk/examples/submit-receipt.ts):

AGNCRED_API_URL=http://localhost:3000 AGNCRED_API_KEY=$AGNCRED_API_KEY \
  npx tsx packages/sdk/examples/submit-receipt.ts

The receipt payload never carries prompts, source code, or customer content — summaries and hashed evidence descriptors only. The private reference is stored separately and erasably; only its hash enters the immutable payload.

5. Invite a reviewer at an unregistered company

curl -s -b /tmp/op.cookies -H "x-csrf-token: $CSRF" \
  -H "Content-Type: application/json" \
  -X POST "http://localhost:3000/api/v1/receipts/$RECEIPT_ID/request-attestation" \
  -d '{"reviewer_email":"reviewer@client.example","company_name":"Client GmbH","message":"Please confirm the July batch."}'

Watch the pnpm dev:api terminal: the console mailer prints the invitation email, including the single-use link (valid 7 days):

--- mail (company_invitation) to reviewer@client.example ---
...
http://localhost:5173/portal/invitations/<invitation-token>
--- end mail ---

6. Claim the invitation and accept the receipt

In the browser: open the printed invitation link, create the company, and the invited receipt appears in the review queue immediately. Or with curl:

INV_TOKEN=paste_invitation_token_here
curl -s -X POST "http://localhost:3000/api/v1/invitations/$INV_TOKEN/claim" \
  -H "Content-Type: application/json" \
  -d '{"display_name":"Riley Reviewer","company_name":"Client GmbH"}'
# -> 201 { "status": "claimed", "company_id": "org_...", "login_token": "..." (dev) }

Sign the reviewer in with the returned login_token (same session exchange as step 2, using a second cookie jar /tmp/rev.cookies), then accept:

REV_CSRF=$(awk '$6=="agncred_csrf" {print $7}' /tmp/rev.cookies)
curl -s -b /tmp/rev.cookies -H "x-csrf-token: $REV_CSRF" \
  -H "Content-Type: application/json" \
  -X POST "http://localhost:3000/api/v1/receipts/$RECEIPT_ID/attest" \
  -d '{
    "action": "accept",
    "independence": "independent",
    "reviewer_quality_score": 90,
    "confirmed_hours_saved": 2,
    "confirmed_outcomes": ["July invoice batch processed and reconciled"],
    "public_summary": "Processed and reconciled the July invoice batch."
  }'

Acceptance signs the immutable receipt version with the company's Ed25519 key (created automatically on first use, encrypted at rest).

7. Verify

curl -s "http://localhost:3000/api/v1/receipts/$RECEIPT_ID/verify"

Expect signature.valid: true (scheme agncred-attestation.v1, with the signing key_id) and event_chain.valid: true. The full offline bundle is at /api/v1/receipts/$RECEIPT_ID/verification-bundle — see VERIFICATION_GUIDE.md to verify it without trusting the server.

Note on score: the new company is unverified, so this receipt correctly contributes zero points until the company completes domain verification (portal: company workspace, or POST /api/v1/company/domain-verification). That is the "no proof, no reputation" rule working, not a bug.

Where things print

Next: API_GUIDE.md for the full ingestion API, MCP_GUIDE.md to submit from Claude Code, N8N_GUIDE.md for the n8n workflow.