Attestiq
Docs · API tour

Docs · API tour

Check every AI draft before it leaves.

Attestiq is one API call after each draft. Your agent sends the draft and the records it was written from. Attestiq returns a verdict, the errors it found, the rule each one breaks, and signed evidence.

Access

Access is by pilot invitation; there is no self-serve signup. The production base URL, https://api.attestiq.ai, is planned. Pilot clients get keys and the current base URL from us. Write to pilot@attestiq.ai.

How it fits into your workflow

Take an agent that drafts chargeback responses. Today someone re-reads every draft. With Attestiq, the agent calls the API after each draft and your workflow routes on the verdict. People review only what is held.

Your systems

Your agent drafts

A chargeback response, a dispute letter, a retry policy. Your case system holds the records.

Attestiq · POST /v1/validate/validations

  1. 1 Reject card numbers, redact names
  2. 2 Extract claims from the draft
  3. 3 Check them against records and rules
  4. 4 Code sets the verdict
  5. 5 Sign the evidence

Your systems act

  • pass · send it
  • pass_with_warnings · send, log notes
  • hold · a reviewer decides
  • fail · back to the agent
Your agent pushes to Attestiq. Attestiq never connects to your systems, pulls data, or sits in the payment flow.

Core concepts

Code decides. Models read. Evidence proves. A person approves. Every verdict is computed by versioned code from the findings. Models are off unless you turn them on. When they are on, they can only add findings that quote the draft and cite a rule, and they never set a verdict, a deadline, or a dollar figure.

Every check is an object. Each call creates a Validation with an id like val_…. You can fetch it later, list it, recompute its evidence, and see who reviewed it.

A Validation has two states. status tracks the run: queued → running → completed (or failed). review_status tracks the people: not_required, or pending → released, blocked, or returned.

Rules are data. Each workload (for example disputes.representment) maps to a policy pack, such as disputes-us@0.2.0. Every finding names its rule, its citation, and whether an expert has reviewed that rule yet (citation.review_status).

Core objects

Six objects cover the whole integration. IDs are prefixed strings; timestamps are ISO-8601 UTC; money is integer cents.

Request lifecycle

What happens between your request and the verdict:

  1. Intake. The output is fingerprinted (SHA-256). Records are checked for card numbers; any full card number rejects the request. Names and contact details are redacted before anything is stored.
  2. Claims. Code extracts the amounts, dates, reason codes, card digits, and outcomes the draft states, each with the sentence it came from.
  3. Checks. Fact, math, policy, and disclosure checks compare the claims with your records and the rules in the pinned policy pack.
  4. Verdict. Code computes the verdict and score from the findings' severities. A model, when enabled, may add cited findings; it never sets the verdict.
  5. Evidence. Each step is written as a signed evidence record; the submission lands on your tenant's audit chain.
  6. Delivery. Sync mode returns the validation in the response. Async mode returns 202 and sends a signed webhook when the verdict is ready.
  7. Review. Hold and fail verdicts wait for a person in your team to release, block, or return the draft. The decision is audited with its rationale.

The verdict follows from the most severe finding that counts (advisory findings never count): any critical finding gives fail; high gives hold; medium gives pass_with_warnings; otherwise pass. The score starts at 100 and loses 45, 20, 8, or 2 points per critical, high, medium, or low finding, down to 0.

Validation states
status          queued ──▶ running ──▶ completed
                                  └──▶ failed      (validator error; see "error")

verdict         pass | pass_with_warnings | hold | fail
                (set once, when status is completed)

review_status   not_required                    (pass, pass_with_warnings)
                pending ──▶ released            (hold, fail)
                        ├─▶ blocked
                        └─▶ returned            (return_to_agent)

Five-minute quickstart

Your first verdict

  1. Check your key. GET /v1/me shows your role, your tenant, and whether the tenant is a synthetic sandbox. Your agent uses a service key: it can submit and read, nothing else.
  2. Send a draft. POST /v1/validate/validations with the workload, your agent's id, the draft, and the records it was written from. The example is a synthetic Mastercard second presentment.
  3. Route on the verdict. This draft is hold, score 60: it was filed seven days after the 45-day window, and it encloses an invoice where a non-receipt dispute needs proof of delivery. Each finding points to the record field and the rule.

Using an MCP-capable agent framework? Add the attestiq-mcp server and the agent gets a validate_output tool. See MCP tools.

The full setup, including async mode, webhooks, and a go-live checklist, is in the quickstart.

1 · Check your key
curl https://api.attestiq.ai/v1/me -H "X-API-Key: $ATTESTIQ_API_KEY"

{
  "key_id": "key_…", "name": "dispute-drafter agent", "email": "",
  "role": "service", "kind": "service",
  "permissions": ["authorize:read", "evidence:read", "policy:read",
                  "validate:read", "validate:submit"],
  "tenant": {"id": "…", "name": "…", "kind": "processor", "synthetic": true},
  "entitlements": {"validate": {"module": "validate", "plan": "pilot", "status": "active",
                                "quota": 10000, "period": "month", "used_this_month": 26, ...}},
  "model_gateway": {"provider": "none", "models": {}, "mode": "deterministic"},
  "version": "0.1.0"
}
2 · Validate a draft
curl https://api.attestiq.ai/v1/validate/validations \
  -H "X-API-Key: $ATTESTIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<'JSON'
{
  "workload": "disputes.representment",
  "agent": {"id": "chargeback-responder", "version": "0.9.0"},
  "output": "Second presentment — Mastercard chargeback CB-77120\n\nMerchant: Lumen Travel Gear. Transaction amount: $249.00 on July 2, 2026, card ending in 8820. ...",
  "records": [
    {"type": "transaction", "id": "TXN-51007", "data": {
      "amount_cents": 24900, "currency": "USD", "transaction_date": "2026-07-02",
      "merchant_name": "Lumen Travel Gear", "network": "mastercard",
      "card_type": "credit", "card_last4": "8820"}},
    {"type": "chargeback", "id": "CB-77120", "data": {
      "network": "mastercard", "reason_code": "4853", "category": "not_received",
      "chargeback_date": "2026-07-28", "amount_cents": 24900}},
    {"type": "representment", "id": "REP-77120", "data": {
      "response_date": "2026-09-18", "evidence": ["invoice"]}}
  ],
  "external_id": "CB-77120",
  "idempotency_key": "CB-77120-draft-1"
}
JSON
3 · Read the verdict
HTTP/1.1 201 Created
X-Request-ID: 3f9c1a7e2b6d4c80

{
  "id": "val_01a0b7f8cd7cd4e8b76923ba",
  "object": "validation",
  "purpose": "production",
  "workload": "disputes.representment",
  "policy_pack": "disputes-us@0.2.0",
  "agent": {"id": "chargeback-responder", "version": "0.9.0", "model": ""},
  "external_id": "CB-77120",
  "status": "completed",
  "verdict": "hold",
  "score": 60,
  "counts": {"total": 2, "binding": 2, "advisory": 0,
             "by_severity": {"high": 2},
             "by_code": {"POLICY.MISSING_REQUIRED_EVIDENCE": 1, "POLICY.TIMELINE_VIOLATION": 1}},
  "review_status": "pending",
  "summary": "Hold — a reviewer must decide before this document is released. Score 60/100. ...",
  "summary_source": "template",
  "output_hash": "sha256:6f892b5aa2f165f537c46ad74ca8cd44a1a1f340d7dc3e0820faa4a8888d95a4",
  "verdict_ref": "EV-2026-1M2VZHKBZWN8S6",
  "evidence_refs": ["EV-2026-1M2VZHKBWYQD81", "EV-2026-1M2VZHKBXJKDCB", "EV-2026-1M2VZHKBYNYCTZ",
                    "EV-2026-1M2VZHKBYRGPYK", "EV-2026-1M2VZHKBZWN8S6"],
  "usage": {"tokens_in": 0, "tokens_out": 0, "steps": 10, "latency_ms": 0, "llm_calls": 0,
            "model_mode": "deterministic", "trace": {"ok": 7, "skipped": 3}},
  "error": null,
  "submitted_by": "service:key_demo_06",
  "created_at": "2026-09-18T16:42:11Z",
  "completed_at": "2026-09-18T16:42:11Z",
  "reviewed_by": null,
  "reviewed_at": null,
  "review_rationale": null,
  "storage_mode": "redacted",
  "content_purged_at": null,
  "findings": [
    { "id": "fnd_01a0b7f8cd803dbb97fe4400", "code": "POLICY.MISSING_REQUIRED_EVIDENCE", "...": "..." },
    {
      "id": "fnd_01a0b7f8cd8083258e369f36",
      "code": "POLICY.TIMELINE_VIOLATION",
      "family": "policy",
      "severity": "high",
      "validator": "policy",
      "assurance": "deterministic",
      "message": "Deadline missed by 7 day(s): Mastercard allows 45 calendar days to respond. Received 2026-07-28, due 2026-09-11, issued 2026-09-18.",
      "quote": "",
      "expected": "on or before 2026-09-11",
      "observed": "2026-09-18",
      "rule_id": "NET-REP-WINDOW",
      "citation": {
        "pack": "disputes-us@0.2.0",
        "rule_id": "NET-REP-WINDOW",
        "cite": "Visa Dispute Management Guidelines (dispute response); Mastercard Chargeback Guide (second presentment)",
        "source": "Visa Core Rules and Visa Product and Service Rules; Visa Dispute Management Guidelines",
        "url": "https://usa.visa.com/support/small-business/dispute-resolution.html",
        "review_status": "unreviewed"
      },
      "record_ref": "chargeback:CB-77120#chargeback_date",
      "confidence": 1.0,
      "related": [],
      "evidence_ref": "EV-2026-1M2VZHKBYRGPYK",
      "label": null,
      "label_note": null,
      "labeled_by": null
    }
  ]
}