Quickstart

Complete this flow with a test key to get a verdict and a checked proof. All examples use https://api.acretix.io and read ACRETIX_API_KEY from the environment.

1. Get an API key

Create a test tenant API key and put it in ACRETIX_API_KEY. A test key works for every step below; no production key is required.

2. Compile a rubric

A rubric is your success criteria. The examples compile a minimal invoice rule and retain its content hash.

3. Verify a submission

Submit work with an Idempotency-Key. wait_ms holds the request open for up to 45 seconds; if it returns queued or running, poll the verification resource until it completes.

4. Fetch the signed proof envelope

A completed verification has an append-only, Ed25519-signed proof envelope. Fetch it using the verification id.

5. Re-verify — trust no one, including us

For a relying-party verification rather than this convenience endpoint, follow the pinned trust root recipe in Verify a proof: independently resolve keys, anchor the server-served proof head, and bind the expected subject.

6. Send feedback (optional)

When you know whether a verdict was right, wrong or disputed, send it with POST /v1/verifications/{id}/feedback. It is counted in your project's own AI record. It never changes a verdict, an escalation or the engine's calibration. This is optional and not part of the quickstart flow.

7. Check the proof

POST the envelope from step 4 with expectedRubricHash set to the hash returned by step 2. A successful response has valid: true.

curl

The curl version uses jq to retain response values.

export API=https://api.acretix.io
export KEY="$ACRETIX_API_KEY"
test -n "$KEY" || { echo 'Set ACRETIX_API_KEY first'; exit 1; }

# 2. Compile a rubric. Keep both values: the hash binds the proof check.
RUBRIC=$(curl -sS -X POST $API/v1/rubrics \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"name":"quickstart-invoice","criteria":[{"key":"has_invoice","type":"must_have","primitive":"regex_match@1","config":{"pattern":"INV-[0-9]+"}}]}')
RUBRIC_ID=$(printf '%s' "$RUBRIC" | jq -r .rubric_id)
RUBRIC_HASH=$(printf '%s' "$RUBRIC" | jq -r .hash)

# 3. Verify a submission. Idempotency-Key makes a retry safe; wait_ms waits up to 45 seconds.
VERIFICATION=$(curl -sS -X POST $API/v1/verify \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-invoice-1" \
  -d "{\"rubric_id\":\"$RUBRIC_ID\",\"submission\":{\"inline\":\"INV-4821 total \$1,250.50\"},\"options\":{\"wait_ms\":45000}}")
ID=$(printf '%s' "$VERIFICATION" | jq -r '.id // .verification_id')
STATUS=$(printf '%s' "$VERIFICATION" | jq -r .status)
while [ "$STATUS" = queued ] || [ "$STATUS" = running ]; do
  sleep 1
  VERIFICATION=$(curl -sS $API/v1/verifications/$ID -H "Authorization: Bearer $KEY")
  STATUS=$(printf '%s' "$VERIFICATION" | jq -r .status)
done
printf '%s\n' "$VERIFICATION" # the completed verdict

# 4. Fetch the signed proof envelope returned for this completed verification.
curl -sS $API/v1/verifications/$ID/proof/envelope -H "Authorization: Bearer $KEY" > envelope.json

# 7. Check the proof envelope. The public endpoint returns { "valid": true } for this envelope and rubric hash.
jq --arg expectedRubricHash "$RUBRIC_HASH" '{ proof: ., expectedRubricHash: $expectedRubricHash }' envelope.json | \
  curl -sS -X POST $API/v1/proofs/verify -H "Content-Type: application/json" --data-binary @-

TypeScript

// Node 18+; fetch is built in. Set ACRETIX_API_KEY in your environment.
const API = 'https://api.acretix.io';
const key = process.env.ACRETIX_API_KEY;
if (!key) throw new Error('Set ACRETIX_API_KEY first');

async function api(path: string, init: RequestInit = {}) {
  const response = await fetch(API + path, {
    ...init,
    headers: { Authorization: 'Bearer ' + key, ...init.headers },
  });
  const body = await response.json();
  if (!response.ok) throw new Error(JSON.stringify(body));
  return body;
}

// 2. Compile a rubric; retain its hash for the proof check.
const rubric = await api('/v1/rubrics', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'quickstart-invoice', criteria: [
    { key: 'has_invoice', type: 'must_have', primitive: 'regex_match@1', config: { pattern: 'INV-[0-9]+' } },
  ] }),
});

// 3. Submit, then poll only when bounded sync returns queued/running.
let verification = await api('/v1/verify', {
  method: 'POST', headers: { 'Content-Type': 'application/json', 'Idempotency-Key': 'quickstart-invoice-1' },
  body: JSON.stringify({ rubric_id: rubric.rubric_id, submission: { inline: 'INV-4821 total $1,250.50' }, options: { wait_ms: 45000 } }),
});
const id = verification.id ?? verification.verification_id;
while (verification.status === 'queued' || verification.status === 'running') {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  verification = await api(`/v1/verifications/${id}`);
}
console.log(verification); // the completed verdict

// 4. Fetch the signed proof envelope, then 7. check it against the compiled rubric hash.
const envelope = await api(`/v1/verifications/${id}/proof/envelope`);
const checked = await api('/v1/proofs/verify', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ proof: envelope, expectedRubricHash: rubric.hash }),
});
if (!checked.valid) throw new Error(checked.reason);
console.log(checked); // { valid: true, ... }

Python

# Python 3 standard library only. Set ACRETIX_API_KEY in your environment.
import json
import os
import time
import urllib.request

API = 'https://api.acretix.io'
KEY = os.environ['ACRETIX_API_KEY']

def api(path, method='GET', body=None, headers=None):
    encoded = json.dumps(body).encode() if body is not None else None
    request = urllib.request.Request(API + path, data=encoded, method=method)
    request.add_header('Authorization', 'Bearer ' + KEY)
    if body is not None:
        request.add_header('Content-Type', 'application/json')
    for name, value in (headers or {}).items():
        request.add_header(name, value)
    with urllib.request.urlopen(request) as response:
        return json.load(response)

# 2. Compile a rubric; retain its hash for the proof check.
rubric = api('/v1/rubrics', 'POST', {
    'name': 'quickstart-invoice',
    'criteria': [{'key': 'has_invoice', 'type': 'must_have', 'primitive': 'regex_match@1',
                  'config': {'pattern': 'INV-[0-9]+'}}],
})

# 3. Submit with an Idempotency-Key and wait_ms, polling only if needed.
verification = api('/v1/verify', 'POST', {
    'rubric_id': rubric['rubric_id'],
    'submission': {'inline': 'INV-4821 total $1,250.50'},
    'options': {'wait_ms': 45000},
}, {'Idempotency-Key': 'quickstart-invoice-1'})
verification_id = verification.get('id', verification.get('verification_id'))
while verification['status'] in ('queued', 'running'):
    time.sleep(1)
    verification = api(f'/v1/verifications/{verification_id}')
print(verification)  # the completed verdict

# 4. Fetch the signed proof envelope, then 7. check it against the compiled rubric hash.
envelope = api(f'/v1/verifications/{verification_id}/proof/envelope')
checked = api('/v1/proofs/verify', 'POST', {
    'proof': envelope,
    'expectedRubricHash': rubric['hash'],
})
if not checked['valid']:
    raise RuntimeError(checked['reason'])
print(checked)  # { 'valid': True, ... }