Two ways, same answer
Open the page
https://orru.xyz/verify/<credentialId>Server-rendered, no wallet, no login. Works in a fresh incognito window. Hand this link to anyone.Call the API
GET /api/verify/<credentialId>The same data as JSON. Public and unauthenticated.What a statement proves
Four checks compose into one statement.1
The Ethereum evidence is authentic
Attestcoin authenticated the source transaction against attested Ethereum history through Creditcoin’s native query verifier. For Demo Payroll that receipt contains the ERC-20 transfer. For Semuni it contains the payer’s commitment, not proof of an off-chain bank transfer.
2
The payer is recognised
The event came from the exact expected source contract, and its indexed payer is approved. The payer comes from
topics[1], not the transaction’s gas-paying from address. A stranger emitting a lookalike event does not count.3
The proof uses the same commitments
The zero-knowledge proof recomputes each commitment from the private amount, period and salt. Creditcoin requires those byte-identical commitments to be the ones Attestcoin accepted, then checks the common band and consecutive periods.
4
The subject controls the wallet
The holder signed an EIP-712 authorization at issuance. A proof shows payments exist; it does not show that whoever submitted it owns the address they named. Both are required.
What it does not prove
It is a dated record of past payments, not a forecast. Treat it as one input to your own decision.What you get, and what you do not
A statement carries an income band, never an amount.
The band is enforced by a zero-knowledge circuit: the holder proves every payment fell inside one band without revealing what any of them were. That is the whole point, and it means a narrower band is more useful to you and less private to them.
Statements do not expire
There is no expiry field, and that is a design decision rather than an omission. A statement attests a dated past window, and that stays true forever. Whether a window is recent enough to lend against is your policy, not ours. A payday lender and a mortgage underwriter will not agree, and neither should be forced into the other’s answer. Every statement carriesevidenceEndDate: the date of the most recent payment it proves. Apply your own maximum age to it.
If you want this enforced on-chain rather than in your code,
DemoCreditPool shows the pattern: a minimumEvidenceHeight floor that rejects anything older, and can only ever be raised.Three states, and only three
valid
valid
Issued and not revoked. Read the band, the period count, and
evidenceEndDate, then apply your own freshness policy.revoked
revoked
The holder or the issuer withdrew it.
revokedAt says when. Do not lend against a revoked statement, regardless of how good the band looks.unknown
unknown
No statement with that id was ever issued. Usually a mistyped id. A credential id is
0x followed by 64 hex characters; anything else is not one.