> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trustgated.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Gate with attestations

> Issue, verify, and check EIP-712 trust attestations. You own the ladder. TrustGate owns the score.

## Liability fence

TrustGate said the wallet scored N at time T under `scoringVersion` V. The protocol chose what N allows. Scores behaviour, not value or safety. This is not a credit decision.

Every gating response repeats that disclaimer. Keep it in your own docs and denial copy. Do not tell a user "TrustGate rejected your loan."

## Endpoints

Base: `https://www.trustgated.xyz`

| Method | Path                 | Job                                       |
| ------ | -------------------- | ----------------------------------------- |
| `POST` | `/api/gating/attest` | Issue a signed attestation                |
| `POST` | `/api/gating/verify` | Fail-closed off-chain verify              |
| `POST` | `/api/gating/check`  | Score + evaluate **your** ladder + attest |

Free on testnet. **No CORS.** Call these from your backend. A browser on another origin will fail the preflight.

On testnet, if the unpaid wallet oracle is down, gating **falls back to a raw score of 50** so the path can be demoed. Mainnet is fail-closed — no fallback. Do not ship a production gate that depends on the 50.

## 1. Issue

```json theme={null}
POST /api/gating/attest
{
  "subject": "0x…",
  "subjectType": "wallet",
  "useClass": "financial_high",
  "chainId": 5042002
}
```

`subjectType` is `wallet` (default) or `token`. Aliases: `wallet` / `token` fields instead of `subject`. Optional `ttlSeconds` is clamped to `[60, 7 days]`. Defaults by `useClass` are 6h / 2d / 7d / 1d for `financial_high` / `governance` / `allowlist` / `display`.

`201` body:

```json theme={null}
{
  "attestation": {
    "attestationId": "att_…",
    "subject": "0x…",
    "subjectType": "wallet",
    "chainId": 5042002,
    "score": 57,
    "tier": "MEDIUM",
    "confidence": 70,
    "scoringVersion": "testnet-wallet-v1.0",
    "environment": "testnet",
    "issuedAt": 1750000000,
    "expiresAt": 1750021600,
    "flagsHash": "0x…",
    "issuer": "0x…",
    "flags": [],
    "signature": "0x…",
    "disclaimer": "TrustGate scores behaviour, not value or safety. …"
  },
  "pricing": "free"
}
```

If you see `note: "Demo signer active"`, you are not production-ready. Set `ATTESTATION_SIGNER_PRIVATE_KEY`. Demo signers are rejected when `SCORING_ENVIRONMENT=mainnet`.

## 2. Verify off-chain

```json theme={null}
POST /api/gating/verify
{
  "attestation": { },
  "expectedSubject": "0x…",
  "expectedChainId": 5042002
}
```

Fail-closed on expiry, future `issuedAt`, `flagsHash` mismatch, unauthorized issuer, bad EIP-712 signature, wrong subject, or wrong chain.

## 3. Check against your ladder

```json theme={null}
POST /api/gating/check
{
  "wallet": "0x…",
  "requestedAmount": 500000,
  "capability": "borrow",
  "useClass": "financial_high",
  "ladder": {
    "protocolId": "my-dao",
    "minConfidence": 40,
    "multiFactorAcknowledged": true,
    "allowedScoringVersions": ["testnet-wallet-v1.0"],
    "bands": [
      { "minScore": 25, "maxScore": 48, "capability": "borrow", "maxAmount": 20000 },
      { "minScore": 49, "maxScore": 60, "capability": "borrow", "maxAmount": 100000 },
      { "minScore": 61, "maxScore": 90, "capability": "borrow", "maxAmount": 500000 }
    ]
  }
}
```

Optional: `tokenAddress` + `tokenLadder` for a dual-signal check.

Response fields that matter:

* `allowed` / `allowedByCallerLadder` — **your** ladder **and** a valid attestation **and** a pinned `scoringVersion`. Still not TrustGate authorization.
* `policySource`: always `"caller_ladder"`
* `scoreIsAuthoritative`: `true` — trust `attestation.score` and `attestation.signature`
* `walletEvaluation`, `tokenEvaluation`
* `attestation`
* `guidance` restates the fence

Demo-only on testnet: `ladderPreset: "example_lending"` or `"example_governance"`. Forbidden on mainnet. Mainnet also requires `protocolId` and multi-factor acknowledgement.

## EIP-712 domain

```text theme={null}
name:    TrustGateAttestation
version: 1
chainId: <attestation.chainId>
```

Primary type `TrustAttestation`:

```text theme={null}
attestationId   string
subject         address
subjectType     string
chainId         uint256
score           uint256
tier            string
confidence      uint256
scoringVersion  string
environment     string
issuedAt        uint256
expiresAt       uint256
flagsHash       bytes32
issuer          address
```

`flagsHash = keccak256(utf8(sortedUppercaseFlags.join("|")))`.

## On-chain verifier

Deploy `contracts/TrustAttestationVerifier.sol` **yourself**. It is implemented in the repo and is **not** a shared singleton on Arc Testnet. Constructor takes the initial issuer. Owner calls `setIssuer(address, bool)` to rotate.

```solidity theme={null}
verifier.verify(attestation, signature, expectedSubject);
```

Reverts: `Expired`, `BadSubject`, `BadChain`, `BadIssuer`, `BadSignature`, `Unauthorized`. The contract does **not** evaluate your ladder. You still apply bands in your own module after a successful verify.

## Integration checklist

* Pin `scoringVersion`
* Set max attestation age (or rely on `expiresAt`)
* Fail closed on missing / invalid / expired attestation
* Require at least one non-TrustGate risk factor for high-value actions
* Write user-facing denials without implying TrustGate "rejected a loan"
* Never present a testnet score as mainnet-gating grade

## Next

<CardGroup cols={2}>
  <Card title="Gating product page" icon="lock" href="/products/gating">
    Doctrine, TTL table, and status.
  </Card>

  <Card title="Protocol Guard" icon="bell" href="/integrate/protocol-guard">
    Alerts and contextual floors, if you do not need a signature.
  </Card>
</CardGroup>
