"Quickstart: REST"
Three curl calls against the hosted service — a credential, a compile, a check — and the exact JSON that comes back.
For anything that speaks HTTPS. Check extracted values against a written policy from a service, a job, or a test, with no SDK.
1. A credential, no sign-up
export DSAIL_CRED=$(curl -s -X POST https://agents.jaxon.ai/v1/credentials/evaluation \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["credential"])')
The token is shown once; the service stores only its hash. Send it on every call as x-jaxon-credential. An evaluation credential is scoped to compile, check and the prompt pack, and capped.
For production, use a production API key in the same header. Ask a connected chat to issue one (dsail_issue_api_key), or call POST /v1/credentials/api-key as a signed-in account. A key reaches the rulesets that account already holds, so a check from your code answers what a check in the chat answers. A call with no credential, or one not recognised, is refused with 401.
2. Compile
cat > expenses.dsail <<'EOF'
version 1.3;
// @ask amount What is the total amount of this expense claim, in USD?
// @unit amount USD
declare amount as numeric;
// @ask has_receipt Is an itemised receipt attached?
declare has_receipt as boolean;
assert receipt_over_75 { Implies(amount > 75 "USD", has_receipt) };
assert within_cap { amount <= 5000 "USD" };
EOF
curl -s -X POST https://agents.jaxon.ai/v1/compile \
-H "x-jaxon-credential: $DSAIL_CRED" -H "content-type: application/json" \
-d "$(python3 -c 'import json; print(json.dumps({"source": open("expenses.dsail").read()}))')"
The response carries ruleset_hash (the content hash addressing exactly these bytes), manifest (every claim with its question, type and unit), claim_schema (JSON Schema for the claim dictionary), the validation contract a check will enforce, and review, a plain-text rendering for a person to sign.
3. Check
curl -s -X POST https://agents.jaxon.ai/v1/check \
-H "x-jaxon-credential: $DSAIL_CRED" -H "content-type: application/json" \
-d '{"ruleset_hash": "<ruleset_hash from step 2>",
"claims": {"amount": "120 USD", "has_receipt": false}}'
{"ok": true,
"ruleset_hash": "<ruleset_hash>", "unit_library_hash": null,
"rules": [
{"rule": "receipt_over_75", "assertions": [
{"name": "receipt_over_75", "check": "FALSE", "unknown_policy": "neutral",
"source": "assert receipt_over_75 { Implies(amount > 75 \"USD\", has_receipt) }"}]},
{"rule": "within_cap", "assertions": [
{"name": "within_cap", "check": "TRUE", "unknown_policy": "neutral",
"source": "assert within_cap { amount <= 5000 \"USD\" }"}]}],
"claims": {"bound": ["amount", "has_receipt"], "unbound": [], "quantities": ["..."]},
"versions": {"wire_version": "2.9.0", "...": "..."}}
Every assertion answers TRUE, FALSE, UNKNOWN or AMBIGUOUS, and each result carries the rule that decided and its source text. There is no combined verdict — fold the results yourself, because what a FALSE costs is your decision. The payload is pure over its inputs (no timestamps, no request id), so the same ruleset and claims return the same bytes every time.
Where the claim values come from
GET /v1/prompt-pack/{ruleset_hash} returns one extraction prompt per claim, the claim schema and the validation rules. Run those prompts on your own model, assemble the claim dictionary, submit it. A claim your extraction could not determine is submitted as the string "unknown" — never a guess, never omitted.
Errors
One envelope for every failure, through every door:
{"ok": false,
"error": {"code": "VALIDATION_REJECTED", "message": "1 field failed validation",
"docs": "https://docs.agents.jaxon.ai/errors/validation-rejected.md",
"failures": [{"field": "amount", "expected": "...", "received": "..."}]},
"versions": {"...": "..."}}
failures names every failing field at once, so one corrected resubmission converges. Error codes lists them all; the full route list is the REST API reference, and the specification is GET https://agents.jaxon.ai/openapi.json.