Documentation
Quickstart
Get your first verdict in 60 seconds. Verify a signup with curl, then wire the same decision into your app with the SDK.
Get your first verdict in 60 seconds
Start with curl to verify a signup, then install the SDK and wire the same decision into your application. Persist the returned eventId; use it later to report what happened. Feedback joins on that id, never on email or IP.
Prefer to see it before touching a terminal? The playground runs the same decision in the browser.
curl -s https://api.chitmark.com/v1/verify \
-H "Authorization: Bearer $CHITMARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "signup",
"session": "sess_quickstart_1",
"surface": "app.acme.com/signup",
"subject": {
"emailSha256": "'"$(printf 'user@example.com' | sha256sum | cut -d" " -f1)"'",
"ipTrunc": "203.0.113.0/24",
"formDataHashed": { "companyDomain": "acme.example" },
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"headers": { "accept": "text/html", "accept-language": "en-US" }
},
"context": { "decisionCosts": { "falseAllow": 1, "falseChallenge": 1, "falseDeny": 100 } }
}' | jq '{decision, actorType, confidence, reasons, eventId, degraded}'0. Get a key
API keys are issued through the public beta: a ck_… value you send as Authorization: Bearer … on every request. Export it before you start:
export CHITMARK_API_KEY=ck_…
# the SDKs and CLI read CHITMARK_API_KEY automatically1. Install
Packages: @chitmark/sdk on npm · @chitmark/cli on npm · chitmark on PyPI
npm install @chitmark/sdk2. Client
Create one shared client. Keep onDegraded: "challenge" (the default).
import { Chitmark } from "@chitmark/sdk";
export const chitmark = new Chitmark({
apiKey: process.env.CHITMARK_API_KEY!,
onDegraded: "challenge", // NEVER "allow"
timeoutMs: 800,
piiMode: "hashed", // default: raw is opt-in
});3. Verify the action
Call verify on signup / trial. Persist eventId on the account row before you branch: feedback joins only on that key.
Your application owns what happens next:
| Decision | What your app does |
|---|---|
allow | Continue: create the account, issue the grant |
challenge | HTTP 428, then issue friction with POST /v1/challenge (or your own step-up) |
deny | Stop: do not create the account or issue the grant |
Only continue on allow when degraded is false. If degraded is true, the decision is always challenge.
{
"decision": "challenge",
"eventId": "evt_01JQ8K3M2N7P9R",
"confidence": 0.84,
"actorType": "likely_farming",
"reasons": ["velocity_burst", "weak_identity"],
"degraded": false,
"verdictToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
}4. Feedback when outcomes land
Feedback is asynchronous. Request path: verify → allow / challenge / deny. Outcome path: account + eventId → outcome → feedback. Report credit_burn, converted, chargeback, abuse_confirmed, etc. when the business fact is known, never during the signup request. Security invariant: feedback cannot create attribution; it only attaches an outcome to an existing verify eventId. Unknown or cross-tenant event ids return 404 unknown_event_id.
await chitmark.feedback({
eventId: account.chitmarkEventId,
outcome: "credit_burn",
value: 12.4,
unit: "usd",
});5. Join the community
Questions, feedback, and the weekly Agent Farming Ledger discussion happen in Discord.